orz-markdown 1.2.2 → 1.3.1
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/README.md +30 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins/chart.d.ts +2 -0
- package/dist/plugins/chart.d.ts.map +1 -0
- package/dist/plugins/chart.js +137 -0
- package/dist/plugins/chart.js.map +1 -0
- package/dist/preview-frame.d.ts +59 -0
- package/dist/preview-frame.d.ts.map +1 -0
- package/dist/preview-frame.js +88 -0
- package/dist/preview-frame.js.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +70 -6
- package/dist/runtime.js.map +1 -1
- package/orz-markdown-skills/SKILL.md +16 -9
- package/orz-markdown-skills/assets/template.html +23 -3
- package/orz-markdown-skills/references/css-classes.md +16 -9
- package/orz-markdown-skills/references/embedding.md +196 -0
- package/orz-markdown-skills/references/syntax.md +5 -3
- package/orz-markdown-skills/references/themes.md +4 -3
- package/package.json +4 -1
- package/themes/README.md +43 -0
- package/themes/beige-decent-1.css +27 -2
- package/themes/beige-decent-2.css +23 -3
- package/themes/dark-elegant-1.css +11 -0
- package/themes/dark-elegant-2.css +17 -9
- package/themes/dark-elegant-3.css +297 -0
- package/themes/light-academic-1.css +49 -1
- package/themes/light-academic-2.css +25 -1
- package/themes/light-neat-1.css +23 -9
- package/themes/light-neat-2.css +8 -5
- package/themes/light-neat-3.css +383 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: orz-markdown
|
|
3
|
-
description: "orz-markdown usage skill. Use this skill whenever you need to render markdown with this parser, write markdown that uses {{...}} custom plugin syntax (mermaid, qrcode, youtube, smiles, toc, span, emoji, attrs, space, yaml, nyml, or their aliases mm/qr/yt/sm/sp/em), use :::container syntax (success/info/warning/danger/spoil/tabs/tab/cols/col/left/right/center), set up a complete HTML page to display parser output, choose or import one of the
|
|
3
|
+
description: "orz-markdown usage skill. Use this skill whenever you need to render markdown with this parser, write markdown that uses {{...}} custom plugin syntax (mermaid, chart, qrcode, youtube, smiles, toc, span, emoji, attrs, space, yaml, nyml, or their aliases mm/qr/yt/sm/sp/em), use :::container syntax (success/info/warning/danger/spoil/tabs/tab/cols/col/left/right/center), set up a complete HTML page to display parser output, choose or import one of the 12 bundled CSS themes, or create a custom theme stylesheet. Also invoke when asked about .markdown-body class, prepareSources, browser runtime scripts for QR codes or tabs, or any KaTeX math syntax in this project. ALWAYS invoke before editing documents whose headings carry stable block IDs ({{attrs[#blk-...]}})."
|
|
4
4
|
compatibility:
|
|
5
5
|
runtime: "Node.js 20+, ESM"
|
|
6
6
|
package: "orz-markdown"
|
|
@@ -8,7 +8,7 @@ compatibility:
|
|
|
8
8
|
|
|
9
9
|
# orz-markdown
|
|
10
10
|
|
|
11
|
-
A deeply customized `markdown-it` instance with 10+ plugins,
|
|
11
|
+
A deeply customized `markdown-it` instance with 10+ custom plugins, 10 official plugin bundles, and 12 ready-to-use CSS themes. All rendered HTML lives inside `<article class="markdown-body">`.
|
|
12
12
|
|
|
13
13
|
## Rendering (Node.js / ESM)
|
|
14
14
|
|
|
@@ -38,14 +38,16 @@ const html = md.render(resolved, { markdownBasePath: '/local/base/path' });
|
|
|
38
38
|
|
|
39
39
|
Every page that displays parser output needs **all five** of these:
|
|
40
40
|
|
|
41
|
-
1. **Theme stylesheet** — one of the
|
|
41
|
+
1. **Theme stylesheet** — one of the 12 bundled themes, or `assets/minimal.css`
|
|
42
42
|
2. **KaTeX CSS** — `https://cdn.jsdelivr.net/npm/katex@0.16.35/dist/katex.min.css`
|
|
43
43
|
3. **Highlight.js CSS** — match light/dark to the chosen theme
|
|
44
|
-
4. **
|
|
44
|
+
4. **Four CDN scripts** — Highlight.js, Mermaid.js, SmilesDrawer, Chart.js (loaded in body)
|
|
45
45
|
5. **Two inline scripts** — tabs initializer and QR code runtime (also in body)
|
|
46
46
|
|
|
47
47
|
**Use `assets/template.html`** — all CDN links, scripts, and the `.markdown-body` wrapper are pre-wired. Copy it and replace the `<!-- INSERT RENDERED HTML HERE -->` comment.
|
|
48
48
|
|
|
49
|
+
**Mounting output in an `<iframe>` (viewer / editor / slide or page engine)?** Call `getPreviewFrameAssets()` from `orz-markdown/preview-frame` — it returns the pinned CDN URLs, the runtime, `headLinks(scheme)` / `bodyScripts()` strings, and a `window.__orzEnhance()` that does all of items 3–5 (highlight code, draw mermaid/SMILES/charts, init tabs + QR). One call instead of re-deriving the wiring. See `references/embedding.md`.
|
|
50
|
+
|
|
49
51
|
CDN URLs (pinned versions):
|
|
50
52
|
| Library | URL |
|
|
51
53
|
|---|---|
|
|
@@ -54,6 +56,7 @@ CDN URLs (pinned versions):
|
|
|
54
56
|
| Highlight.js dark CSS | `.../styles/atom-one-dark.min.css` |
|
|
55
57
|
| Mermaid.js | `https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js` |
|
|
56
58
|
| SmilesDrawer | `https://unpkg.com/smiles-drawer@1.0.10/dist/smiles-drawer.min.js` |
|
|
59
|
+
| Chart.js | `https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js` |
|
|
57
60
|
|
|
58
61
|
### Browser runtime API
|
|
59
62
|
|
|
@@ -69,20 +72,22 @@ document.body.appendChild(script);
|
|
|
69
72
|
|
|
70
73
|
The runtime also provides **copy-as-Markdown**: with it loaded, copying a selection inside `.markdown-body` puts Markdown source on the clipboard, not HTML (tables, lists, math, code, etc. are reconstructed). It skips selections inside `<input>`/`<textarea>`/`contenteditable`. Convert a node directly with `window.OrzMarkdownRuntime.elementToMarkdown(node)`.
|
|
71
74
|
|
|
72
|
-
> **Do not strip `data-md` attributes.** `mermaid`, `smiles`, `qrcode`, and `
|
|
75
|
+
> **Do not strip `data-md` attributes.** `mermaid`, `smiles`, `qrcode`, `youtube`, and `chart` output carry a `data-md` breadcrumb so copy recovers their source after client-side rendering (e.g. a copied QR yields `{{qr ...}}`, not its SVG). Preserve these attributes if you post-process the HTML.
|
|
73
76
|
|
|
74
77
|
---
|
|
75
78
|
|
|
76
79
|
## Themes
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
Twelve bundled themes — each auto-imports `common.css` (structural rules for tables, images, QR overlays, print).
|
|
79
82
|
|
|
80
83
|
| File | Style | Scheme |
|
|
81
84
|
|---|---|---|
|
|
82
85
|
| `dark-elegant-1.css` | Cinzel headings · scholarly serif | Dark |
|
|
83
86
|
| `dark-elegant-2.css` | Dark elegant variant | Dark |
|
|
87
|
+
| `dark-elegant-3.css` | Lora · VS Code-dark, colourful headings "Nocturne" | Dark |
|
|
84
88
|
| `light-neat-1.css` | Figtree · clean modern sans | Light |
|
|
85
89
|
| `light-neat-2.css` | Light neat variant | Light |
|
|
90
|
+
| `light-neat-3.css` | Bricolage · calm green "Orchard" | Light |
|
|
86
91
|
| `beige-decent-1.css` | Warm beige · print-like prose | Light |
|
|
87
92
|
| `beige-decent-2.css` | Beige decent variant | Light |
|
|
88
93
|
| `light-academic-1.css` | Alegreya · justified scholarly prose | Light |
|
|
@@ -98,7 +103,7 @@ import 'orz-markdown/themes/light-neat-1.css';
|
|
|
98
103
|
// <link rel="stylesheet" href="node_modules/orz-markdown/themes/light-neat-1.css">
|
|
99
104
|
```
|
|
100
105
|
|
|
101
|
-
For custom themes start from `assets/minimal.css` (structural only, no decoration) and add your visual layer. See `references/themes.md` for the design token pattern, element checklist, and design guidelines. See `references/css-classes.md` for the full list of every CSS class the parser emits.
|
|
106
|
+
For custom themes start from `assets/minimal.css` (structural only, no decoration) and add your visual layer. See `references/themes.md` for the design token pattern, element checklist, and design guidelines. See `references/css-classes.md` for the full list of every CSS class the parser emits. **Building a host app that supplies its own CSS and page shell (a slide engine, viewer, or editor)?** Read `references/embedding.md` — it gathers the CSS contract, the JS runtime + diagram-drawing duties, and the copy-as-Markdown requirements into one checklist (and the non-obvious gotchas, like a host CSS reset stripping bold/sub/sup).
|
|
102
107
|
|
|
103
108
|
---
|
|
104
109
|
|
|
@@ -115,11 +120,12 @@ Escape with backslash: `\{{name}}` renders as literal `{{name}}`.
|
|
|
115
120
|
|---|---|---|
|
|
116
121
|
| **span** | `sp` | `{{sp[red] colored}}` · `{{sp[success] ✓ Done}}` |
|
|
117
122
|
| **emoji** | `em` | `{{emoji wave}}` · `{{em tada}}` |
|
|
118
|
-
| **space** | — | `{{space 4}}` →
|
|
123
|
+
| **space** | — | `{{space 4}}` → 4rem-wide inline-block spacer |
|
|
119
124
|
| **qrcode** | `qr` | `{{qr https://example.com}}` (click-to-expand SVG) |
|
|
120
125
|
| **youtube** | `yt` | `{{youtube dQw4w9WgXcQ}}` (responsive iframe) |
|
|
121
126
|
| **mermaid** | `mm` | `{{mm\ngraph LR\nA-->B\n}}` |
|
|
122
127
|
| **smiles** | `sm` | `{{smiles C1=CC=CC=C1}}` (chemical structure) |
|
|
128
|
+
| **chart** | — | `{{chart\ntype: bar\nlabels: A, B\ndata: 1, 2\n}}` (Chart.js bar/line/pie/doughnut) |
|
|
123
129
|
| **toc** | — | `{{toc}}` or `{{toc 2,3}}` (heading levels) |
|
|
124
130
|
| **attrs** | — | `# Title{{attrs[id="hero"]}}` |
|
|
125
131
|
| **markdown** | `md`, `md-include` | `{{md ./path/to/file.md}}` |
|
|
@@ -173,7 +179,8 @@ Documents may carry stable block IDs on headings: `## Title{{attrs[#blk-abc12345
|
|
|
173
179
|
|---|---|
|
|
174
180
|
| `references/syntax.md` | Full syntax with examples for every plugin, container, and extended markdown feature |
|
|
175
181
|
| `references/block-ids.md` | Stable block-ID preservation rules — REQUIRED reading before editing documents that use `{{attrs[#blk-...]}}` |
|
|
176
|
-
| `references/themes.md` | Custom theme guide: design tokens, element checklist,
|
|
182
|
+
| `references/themes.md` | Custom theme guide: design tokens, element checklist, 12 design guidelines |
|
|
177
183
|
| `references/css-classes.md` | Every CSS class and HTML element the parser emits — the spec for theme authors |
|
|
184
|
+
| `references/embedding.md` | Host-integration guide for apps that bring their own CSS/shell (slide engines, viewers, editors): the CSS contract + reset gotchas, the JS runtime + diagram drawing, and what copy-as-Markdown needs |
|
|
178
185
|
| `assets/template.html` | Ready-to-use HTML page template with all scripts and CDN links wired up |
|
|
179
186
|
| `assets/minimal.css` | Structural-only stylesheet — starting point for custom themes |
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- Parser stylesheet — choose one of the bundled themes or link your own.
|
|
21
21
|
Bundled themes (each imports common.css automatically):
|
|
22
|
-
dark-elegant-1.css dark-elegant-2.css
|
|
23
|
-
light-neat-1.css light-neat-2.css
|
|
22
|
+
dark-elegant-1.css dark-elegant-2.css dark-elegant-3.css
|
|
23
|
+
light-neat-1.css light-neat-2.css light-neat-3.css
|
|
24
24
|
beige-decent-1.css beige-decent-2.css
|
|
25
25
|
light-academic-1.css light-academic-2.css
|
|
26
26
|
light-playful-1.css light-playful-2.css
|
|
@@ -64,6 +64,26 @@ if (typeof SmilesDrawer !== 'undefined') {
|
|
|
64
64
|
}
|
|
65
65
|
</script>
|
|
66
66
|
|
|
67
|
+
<!-- Chart.js — renders {{chart}} canvases emitted by the parser. -->
|
|
68
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
|
|
69
|
+
<script>
|
|
70
|
+
if (typeof Chart !== 'undefined') {
|
|
71
|
+
document.querySelectorAll('canvas.orz-chart[data-chart]').forEach(function (canvas) {
|
|
72
|
+
var wrap = document.createElement('div');
|
|
73
|
+
wrap.style.cssText = 'position:relative;width:100%;max-width:520px;margin:.6em auto';
|
|
74
|
+
canvas.parentNode.insertBefore(wrap, canvas);
|
|
75
|
+
wrap.appendChild(canvas);
|
|
76
|
+
canvas.removeAttribute('width');
|
|
77
|
+
canvas.removeAttribute('height');
|
|
78
|
+
try {
|
|
79
|
+
var config = JSON.parse(canvas.getAttribute('data-chart') || '{}');
|
|
80
|
+
config.options = Object.assign({ responsive: true, maintainAspectRatio: true, animation: false }, config.options || {});
|
|
81
|
+
new Chart(canvas, config);
|
|
82
|
+
} catch (e) {}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
</script>
|
|
86
|
+
|
|
67
87
|
<!-- Tabs component — builds the .tabs-bar and activates .tab panels.
|
|
68
88
|
Must run after the DOM is painted so .tabs elements are found. -->
|
|
69
89
|
<script>
|
|
@@ -184,7 +204,7 @@ if (typeof SmilesDrawer !== 'undefined') {
|
|
|
184
204
|
// ---- copy-as-markdown: DOM -> Markdown walker ---------------------------
|
|
185
205
|
// Converts a selected DOM fragment back to Markdown so that copying rendered
|
|
186
206
|
// content yields source, not HTML. Standard constructs are reconstructed from
|
|
187
|
-
// their tags; generated constructs (math, mermaid, smiles, qr, youtube) carry
|
|
207
|
+
// their tags; generated constructs (math, mermaid, smiles, qr, youtube, chart) carry
|
|
188
208
|
// a [data-md] breadcrumb that is emitted verbatim. Written without template
|
|
189
209
|
// literals/backticks because this whole file is a String.raw template.
|
|
190
210
|
var BT = String.fromCharCode(96);
|
|
@@ -193,23 +193,30 @@ span.qrcode { background: #fff; padding: 4px; /* or 6px */ vertical-align: middl
|
|
|
193
193
|
|
|
194
194
|
Note: `span.success/info/warning/danger` also have `display: inline-flex; align-items: center; line-height: 1; white-space: nowrap` set by `common.css`. Add `padding`, `border-radius`, `font-size`, and colors in the theme.
|
|
195
195
|
|
|
196
|
+
### Chart
|
|
197
|
+
|
|
198
|
+
| Class | Element | Description |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| `canvas.orz-chart` | `<canvas>` | Carries a Chart.js config as HTML-escaped JSON in `data-chart` (and a `data-md` breadcrumb). The host runtime reads `data-chart` and calls Chart.js. Cap its width. |
|
|
201
|
+
|
|
202
|
+
```css
|
|
203
|
+
canvas.orz-chart { max-width: 100%; height: auto; }
|
|
204
|
+
```
|
|
205
|
+
|
|
196
206
|
### Table of Contents
|
|
197
207
|
|
|
198
208
|
| Class | Element | Description |
|
|
199
209
|
|---|---|---|
|
|
200
|
-
| `.toc-
|
|
201
|
-
| `nav.toc` / `.toc-list` | `<nav>` | The `<ul>` tree of heading links. Reset list bullets in TOC context. |
|
|
210
|
+
| `.toc-list` | `<ul>` | The flat `<ul>` of heading links emitted by `{{toc}}`. Each `<li>` carries an inline `padding-left` for its depth. Reset list bullets. This is the **only** class the parser emits for the TOC. |
|
|
202
211
|
|
|
203
212
|
```css
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
nav.toc ul, .toc-list ul { margin: 0; padding-left: 1.1rem; }
|
|
209
|
-
nav.toc li, .toc-list li { list-style: none; margin-bottom: 0.25rem; }
|
|
210
|
-
nav.toc a, .toc-list a { text-decoration: none; color: var(--link); }
|
|
213
|
+
.toc-list { padding: ...; border: 1px solid var(--border); list-style: none; }
|
|
214
|
+
.toc-list li { list-style: none; margin-bottom: 0.25rem; }
|
|
215
|
+
.toc-list a { text-decoration: none; color: var(--link); }
|
|
211
216
|
```
|
|
212
217
|
|
|
218
|
+
> Note: the parser does **not** emit a `<nav class="toc">` wrapper or a `.toc-wrapper`. `{{toc}}` produces a single `<ul class="toc-list">`. If your host wants a wrapper, add it around the rendered TOC yourself.
|
|
219
|
+
|
|
213
220
|
### Test elements (development only)
|
|
214
221
|
|
|
215
222
|
| Class | Element | Description |
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Embedding orz-markdown in a host app (custom stylesheet)
|
|
2
|
+
|
|
3
|
+
Read this when you call `md.render()` and supply **your own CSS and page shell**
|
|
4
|
+
instead of using a bundled theme — e.g. a slide engine, a doc viewer, an editor,
|
|
5
|
+
or any app that embeds rendered output. The bundled themes already do everything
|
|
6
|
+
below; this guide is the checklist for when you *don't* use them.
|
|
7
|
+
|
|
8
|
+
> The sibling `orz-slides` project hit a long tail of bugs (unstyled containers,
|
|
9
|
+
> bold that changed colour instead of weight, broken sub/sup, diagrams
|
|
10
|
+
> overflowing, copy losing `{{sp}}`/`:::` source) precisely because these points
|
|
11
|
+
> weren't gathered in one place. Follow this and you skip all of it.
|
|
12
|
+
|
|
13
|
+
There are **three** things to get right: CSS, JavaScript, and copy-as-Markdown.
|
|
14
|
+
|
|
15
|
+
> **Shortcut for iframe hosts — `getPreviewFrameAssets()`.** Most of the
|
|
16
|
+
> JavaScript below is the same everywhere, so it lives in one helper:
|
|
17
|
+
>
|
|
18
|
+
> ```js
|
|
19
|
+
> import { getPreviewFrameAssets } from 'orz-markdown/preview-frame';
|
|
20
|
+
> const pa = getPreviewFrameAssets();
|
|
21
|
+
> ```
|
|
22
|
+
>
|
|
23
|
+
> It returns the pinned CDN URLs (`pa.cdn`), the runtime (`pa.runtimeScript`), and
|
|
24
|
+
> ready-made strings: `pa.headLinks(scheme)` (KaTeX + highlight.js CSS, the hljs
|
|
25
|
+
> link carries `id="orz-hljs"`) for the iframe `<head>`, and `pa.bodyScripts()`
|
|
26
|
+
> (loads the libs + runtime and defines `window.__orzEnhance()`) for the `<body>`.
|
|
27
|
+
> After each render call `window.__orzEnhance()` — it highlights code, runs
|
|
28
|
+
> mermaid, draws SMILES (honoring `window.__orzSmilesTheme`) and charts, and
|
|
29
|
+
> re-inits the runtime (tabs + QR). On theme change swap
|
|
30
|
+
> `doc.getElementById('orz-hljs').href = pa.hljsCss(scheme)`. `__orzEnhance`
|
|
31
|
+
> scopes to `.markdown-body`, so it works whatever id/wrapper you use. The rest of
|
|
32
|
+
> this page is what that helper does under the hood — read it if you hand-roll the
|
|
33
|
+
> wiring or need to understand a failure.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. CSS
|
|
38
|
+
|
|
39
|
+
The parser emits plain HTML plus plugin/container classes. Two reference docs
|
|
40
|
+
have the full detail:
|
|
41
|
+
|
|
42
|
+
- `references/css-classes.md` — every class and element the parser emits.
|
|
43
|
+
- `references/themes.md` — the element checklist and design guidance.
|
|
44
|
+
|
|
45
|
+
When you bring your own stylesheet, the four things that bite hardest:
|
|
46
|
+
|
|
47
|
+
### 1a. Wrap output in `.markdown-body` and scope your rules to it
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<article class="markdown-body"> …md.render() output… </article>
|
|
51
|
+
```
|
|
52
|
+
Every theme rule is scoped under `.markdown-body`; copy-as-Markdown also keys off
|
|
53
|
+
this class (§3). For a region-based layout, each rendered region gets its own
|
|
54
|
+
`.markdown-body`.
|
|
55
|
+
|
|
56
|
+
### 1b. A host CSS *reset* will strip inline semantics — restore them
|
|
57
|
+
|
|
58
|
+
This is the #1 non-obvious failure. Many app/framework resets (notably
|
|
59
|
+
**reveal.js**'s `reset.css`) apply to `strong, b, em, i, sub, sup, small, …`:
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
em, strong, sub, sup, b, i, small, … { font: inherit; font-size: 100%; vertical-align: baseline; }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`font: inherit` wipes `font-weight`/`font-style`, and `vertical-align: baseline`
|
|
66
|
+
+ `font-size: 100%` flattens sub/sup. The result: **bold isn't bold, italic
|
|
67
|
+
isn't italic, H~2~O / x^2^ render flat.** (A theme that "fixed" bold with
|
|
68
|
+
`strong { color: accent }` then makes bold *vanish* wherever the accent equals
|
|
69
|
+
the background.) If your host has any such reset, restore the semantics
|
|
70
|
+
explicitly — **bold is weight, not colour**:
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
.markdown-body strong, .markdown-body b { font-weight: 700; }
|
|
74
|
+
.markdown-body em, .markdown-body i { font-style: italic; }
|
|
75
|
+
.markdown-body s, .markdown-body del { text-decoration: line-through; }
|
|
76
|
+
.markdown-body ins,.markdown-body u { text-decoration: underline; }
|
|
77
|
+
.markdown-body sub, .markdown-body sup { font-size: 0.75em; line-height: 0; position: relative; vertical-align: baseline; }
|
|
78
|
+
.markdown-body sub { bottom: -0.3em; }
|
|
79
|
+
.markdown-body sup { top: -0.5em; }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 1c. Style **every** plugin/container class, or it renders unstyled
|
|
83
|
+
|
|
84
|
+
If you don't ship the bundled `common.css`/theme, these have **no** styling and
|
|
85
|
+
look broken. Minimum set (see `css-classes.md` for the exact selectors):
|
|
86
|
+
|
|
87
|
+
| Construct | Selectors to style |
|
|
88
|
+
|---|---|
|
|
89
|
+
| Admonitions | `div.info`, `div.success`, `div.warning`, `div.danger` |
|
|
90
|
+
| Spans — colour | `span.red`, `span.green`, `span.blue`, `span.yellow` (colour only) |
|
|
91
|
+
| Spans — badge | `span.success/info/warning/danger` — **need** `display:inline-flex; align-items:center; line-height:1; white-space:nowrap` + padding/radius (normally in `common.css`) |
|
|
92
|
+
| Columns | `.cols` (CSS grid), `.col` |
|
|
93
|
+
| Tabs | `.tabs`, `.tabs-bar`, `.tabs-bar-btn`(`.active`), `.tab`; **`.tabs:not([data-js]) .tab { display:block }`** (no-JS fallback) and `.tabs[data-js] .tab{display:none}` / `.tab.active{display:block}` |
|
|
94
|
+
| Spoiler | `details.spoil`, `summary` |
|
|
95
|
+
| Alignment | `div.left`, `div.center`, `div.right` (`text-align`) |
|
|
96
|
+
| QR | `span.qrcode` — **must** have `background:#fff; padding` (the SVG is black-on-transparent) |
|
|
97
|
+
| YouTube | `.youtube-embed` — the iframe has **no width/height**; make it a 16:9 responsive box or it defaults to 300×150 |
|
|
98
|
+
| Math | `.katex`, `.katex-display` (load `katex.min.css`) |
|
|
99
|
+
| Diagrams/chem | `.mermaid svg`, `.smiles-render canvas` / `canvas[data-smiles]` |
|
|
100
|
+
|
|
101
|
+
### 1d. Constrain generated graphics so they fit
|
|
102
|
+
|
|
103
|
+
Mermaid SVGs and smiles/chart canvases have intrinsic sizes and will overflow a
|
|
104
|
+
bounded container. At minimum cap the width; in a fixed-size layout (slides),
|
|
105
|
+
size them to the container in JS (measure the container, set explicit
|
|
106
|
+
width/height by the SVG's `viewBox` aspect; for Chart.js use `responsive:false`
|
|
107
|
+
+ explicit `resize(w,h)` because it can't read a definite height through a CSS
|
|
108
|
+
grid). Mermaid also sets an inline `max-width` you may need to override.
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
.markdown-body .mermaid svg,
|
|
112
|
+
.markdown-body canvas[data-smiles],
|
|
113
|
+
.markdown-body canvas.orz-chart { max-width: 100%; height: auto; }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 2. JavaScript
|
|
119
|
+
|
|
120
|
+
Rendering is pure HTML, but several constructs need client-side JS **after mount**.
|
|
121
|
+
|
|
122
|
+
### 2a. Load the runtime — tabs, QR expand, and copy
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
import { getBrowserRuntimeScript } from 'orz-markdown/runtime';
|
|
126
|
+
const s = document.createElement('script');
|
|
127
|
+
s.textContent = getBrowserRuntimeScript();
|
|
128
|
+
document.body.appendChild(s);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
It auto-runs `OrzMarkdownRuntime.init(document)` **once** on `DOMContentLoaded`
|
|
132
|
+
and wires the global copy handler. It exposes
|
|
133
|
+
`OrzMarkdownRuntime.{ init, initTabs, initQrCodes, elementToMarkdown }`.
|
|
134
|
+
|
|
135
|
+
**If you render content dynamically** (after load, or re-render — e.g. an editor
|
|
136
|
+
or a slide engine), the one-time `init` won't see it. Call
|
|
137
|
+
`OrzMarkdownRuntime.init(newRoot)` (or `initTabs`/`initQrCodes`) on the new
|
|
138
|
+
content yourself. `initTabs` guards on `data-js="1"` — if you also have your own
|
|
139
|
+
tab init, use the **same** `data-js="1"` marker so the two don't both build a bar
|
|
140
|
+
(double tab bar bug).
|
|
141
|
+
|
|
142
|
+
### 2b. The runtime does **not** draw diagrams/charts — you do
|
|
143
|
+
|
|
144
|
+
mermaid, smiles, and chart canvases are placeholders. Load the libraries and draw
|
|
145
|
+
them (the bundled themes/`tests/example.html` show the calls):
|
|
146
|
+
|
|
147
|
+
| Construct | You must load | Then |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `.mermaid` | mermaid.js | `mermaid.run({ querySelector: '.mermaid' })` |
|
|
150
|
+
| `canvas[data-smiles]` | smiles-drawer | `SmilesDrawer.parse(...)` → `drawer.draw(tree, canvas, scheme, false)` — pass `'dark'` on dark backgrounds so bonds are light |
|
|
151
|
+
| `canvas.orz-chart` | Chart.js | `new Chart(canvas, JSON.parse(canvas.dataset.chart))` |
|
|
152
|
+
| `$…$` / `$$…$$` | KaTeX **CSS** only | math is pre-rendered by `md.render()`; you just need `katex.min.css` |
|
|
153
|
+
|
|
154
|
+
Draw on first display and re-draw when the container resizes; canvases drawn
|
|
155
|
+
while their container is hidden (`display:none`) size to 0.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 3. Copy-as-Markdown
|
|
160
|
+
|
|
161
|
+
Selecting rendered content and copying yields **Markdown source** when (and only
|
|
162
|
+
when) all of this holds:
|
|
163
|
+
|
|
164
|
+
1. **The runtime is loaded** (§2a) — it installs the `copy` handler and the
|
|
165
|
+
DOM→Markdown walker (`elementToMarkdown`).
|
|
166
|
+
2. **Content is inside `.markdown-body`** (or an element with `data-orz-copy`).
|
|
167
|
+
The handler ignores selections elsewhere and inside inputs/textareas.
|
|
168
|
+
3. **`data-md` breadcrumbs are preserved.** Generated constructs whose source is
|
|
169
|
+
otherwise lost — `mermaid`, `smiles`, `qrcode`, `youtube`, `chart` (and any
|
|
170
|
+
plugin that adds one) — carry `data-md` with their original directive. The
|
|
171
|
+
walker emits it verbatim. **Never strip `data-md`** when you post-process HTML.
|
|
172
|
+
4. **Plugin/container classes are preserved.** Constructs without a `data-md`
|
|
173
|
+
are recovered *by class/structure*: `<span class="red">` → `{{sp[red] …}}`,
|
|
174
|
+
`<div class="center">` → `::: center … :::`, nested `cols`/`tabs` get the
|
|
175
|
+
right fence length, and a `{{toc}}` is recovered as its rendered list of
|
|
176
|
+
heading links (not the `{{toc}}` directive). If you rewrite/strip these
|
|
177
|
+
classes, copy loses the source.
|
|
178
|
+
|
|
179
|
+
Caveat: a container (`::: center`, `:::: cols`, spoiler, tabs) is recovered only
|
|
180
|
+
when the **selection includes the container element**, not just the text inside
|
|
181
|
+
it — select the whole block/region, not the inner words.
|
|
182
|
+
|
|
183
|
+
Convert a node directly: `OrzMarkdownRuntime.elementToMarkdown(node)`.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Pre-flight checklist
|
|
188
|
+
|
|
189
|
+
- [ ] Output wrapped in `.markdown-body`; rules scoped under it.
|
|
190
|
+
- [ ] Inline semantics restored if a host reset touches `strong/em/sub/sup/…` (§1b).
|
|
191
|
+
- [ ] Every admonition / span / cols / tabs / spoil / align / qr / youtube / math / diagram class is styled (§1c, `css-classes.md`).
|
|
192
|
+
- [ ] Span badges have the inline-flex structural CSS; QR has a white plate; tabs have the no-JS fallback; YouTube is a 16:9 box.
|
|
193
|
+
- [ ] Graphics are width-capped (and size-fitted in fixed layouts) (§1d).
|
|
194
|
+
- [ ] Runtime loaded; `init` re-run on dynamically rendered content; tab `data-js="1"` aligned (§2a).
|
|
195
|
+
- [ ] mermaid / smiles-drawer / Chart.js loaded and drawn; KaTeX CSS loaded (§2b).
|
|
196
|
+
- [ ] `data-md` and plugin/container classes preserved end-to-end for copy (§3).
|
|
@@ -310,10 +310,12 @@ Uses `node-emoji` for name lookup. The emoji text alias (e.g., `wave`, `smile`)
|
|
|
310
310
|
### space — Inline horizontal whitespace
|
|
311
311
|
|
|
312
312
|
```markdown
|
|
313
|
-
{{space 4}} →
|
|
314
|
-
{{space 1}} →
|
|
313
|
+
{{space 4}} → 4rem-wide inline-block spacer
|
|
314
|
+
{{space 1}} → 1rem-wide inline-block spacer
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
+
The numeric argument is a `rem` width: `{{space 4}}` emits `<span style="display:inline-block;width:4rem"></span>`. Non-positive or non-numeric values render nothing.
|
|
318
|
+
|
|
317
319
|
---
|
|
318
320
|
|
|
319
321
|
### qrcode / qr — Inline QR code
|
|
@@ -381,7 +383,7 @@ Renders `<div class="smiles-render"><canvas data-smiles="...">`. SmilesDrawer.js
|
|
|
381
383
|
{{toc 1,4}}
|
|
382
384
|
```
|
|
383
385
|
|
|
384
|
-
Auto-generates a `<
|
|
386
|
+
Auto-generates a `<ul class="toc-list">` of heading links (each `<li>` indented by depth). Optional argument is a comma-separated level range (`min,max`), defaulting to `1,3`. The placement in the source does not matter — a core rule scans the whole document, so `{{toc}}` works at the top or bottom.
|
|
385
387
|
|
|
386
388
|
---
|
|
387
389
|
|
|
@@ -99,8 +99,8 @@ Follow this section order in every theme file for consistency:
|
|
|
99
99
|
12. Semantic containers: `.success`, `.info`, `.warning`, `.danger`
|
|
100
100
|
13. Layout utilities: `.left`, `.right`, `.center`
|
|
101
101
|
14. Spoiler, tabs, and columns: `details.spoil`, `.tabs`, `.cols`
|
|
102
|
-
15. TOC and footnotes:
|
|
103
|
-
16. Plugin output: `.youtube-embed`, `.mermaid`, `.smiles-render`, `.qrcode`, span color/badge classes
|
|
102
|
+
15. TOC and footnotes: `.toc-list`, `.footnotes`
|
|
103
|
+
16. Plugin output: `.youtube-embed`, `.mermaid`, `.smiles-render`, `canvas.orz-chart`, `.qrcode`, span color/badge classes
|
|
104
104
|
17. Decorative pseudo-elements and theme-only flourishes
|
|
105
105
|
18. Responsive rules (`@media`)
|
|
106
106
|
19. Print rules (`@media print`)
|
|
@@ -165,12 +165,13 @@ Every theme must have CSS rules for all of the following. See `references/css-cl
|
|
|
165
165
|
- [ ] `.tabs` `.tabs-bar` `.tabs-bar-btn` `.tabs-bar-btn.active` `.tab` `.tab.active`
|
|
166
166
|
- [ ] `.tabs:not([data-js]) .tab` — no-JS fallback: show all panels
|
|
167
167
|
- [ ] `.cols` `.col`
|
|
168
|
-
- [ ]
|
|
168
|
+
- [ ] `.toc-list` — list reset, link styles (the parser emits only `<ul class="toc-list">`, no `nav.toc` wrapper)
|
|
169
169
|
- [ ] `span.red` `span.yellow` `span.green` `span.blue`
|
|
170
170
|
- [ ] `span.success` `span.info` `span.warning` `span.danger` — inline badge variant
|
|
171
171
|
- [ ] `div.youtube-embed` `div.youtube-embed iframe` — 16:9 responsive wrapper (structural)
|
|
172
172
|
- [ ] `div.mermaid` `div.mermaid svg`
|
|
173
173
|
- [ ] `div.smiles-render` `canvas[data-smiles]`
|
|
174
|
+
- [ ] `canvas.orz-chart` — width-capped (Chart.js draws into it client-side)
|
|
174
175
|
- [ ] `span.qrcode` — **must have `background: #fff`** (SVG is always black-on-white)
|
|
175
176
|
|
|
176
177
|
---
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orz-markdown",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Customized markdown-it parser with official and custom plugins",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./dist/index.js",
|
|
9
9
|
"./runtime": "./dist/runtime.js",
|
|
10
|
+
"./preview-frame": "./dist/preview-frame.js",
|
|
10
11
|
"./themes/*": "./themes/*"
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
@@ -64,7 +65,9 @@
|
|
|
64
65
|
"@types/markdown-it-footnote": "^3.0.4",
|
|
65
66
|
"@types/node": "^25.3.5",
|
|
66
67
|
"@types/qrcode-svg": "^1.1.5",
|
|
68
|
+
"esbuild": "^0.21.5",
|
|
67
69
|
"happy-dom": "^15.11.0",
|
|
70
|
+
"path-browserify": "^1.0.1",
|
|
68
71
|
"tsx": "^4.21.0",
|
|
69
72
|
"typescript": "^5.9.3",
|
|
70
73
|
"vitest": "^4.0.18"
|
package/themes/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# orz-markdown themes
|
|
2
|
+
|
|
3
|
+
Twelve ready-to-use CSS themes for the parser's output. All rendered HTML lives
|
|
4
|
+
inside `<article class="markdown-body">`, so every theme is scoped to that class.
|
|
5
|
+
Each theme `@import`s the shared `common.css` (structural rules for tables,
|
|
6
|
+
images, KaTeX blocks, QR overlays, tabs/columns, and print), so you only ever
|
|
7
|
+
import a **single** theme file.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// With a bundler:
|
|
11
|
+
import 'orz-markdown/themes/light-academic-1.css';
|
|
12
|
+
|
|
13
|
+
// Or over a CDN (jsDelivr):
|
|
14
|
+
// https://cdn.jsdelivr.net/npm/orz-markdown/themes/light-academic-1.css
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Bundled themes
|
|
18
|
+
|
|
19
|
+
| File | Style | Scheme |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `light-neat-1.css` | Figtree · clean modern sans | Light |
|
|
22
|
+
| `light-neat-2.css` | Light neat variant | Light |
|
|
23
|
+
| `light-neat-3.css` | Bricolage · calm green — "Orchard" | Light |
|
|
24
|
+
| `light-academic-1.css` | Alegreya · justified scholarly prose | Light |
|
|
25
|
+
| `light-academic-2.css` | Light academic variant | Light |
|
|
26
|
+
| `beige-decent-1.css` | Warm beige · print-like prose | Light |
|
|
27
|
+
| `beige-decent-2.css` | Beige decent variant | Light |
|
|
28
|
+
| `light-playful-1.css` | Casual · personal blog | Light |
|
|
29
|
+
| `light-playful-2.css` | Light playful variant | Light |
|
|
30
|
+
| `dark-elegant-1.css` | Cinzel headings · scholarly serif | Dark |
|
|
31
|
+
| `dark-elegant-2.css` | Dark elegant variant | Dark |
|
|
32
|
+
| `dark-elegant-3.css` | Lora · VS Code-dark, colourful headings — "Nocturne" | Dark |
|
|
33
|
+
|
|
34
|
+
`common.css` is not a theme — it is imported automatically by each theme above.
|
|
35
|
+
|
|
36
|
+
## Theming your own output
|
|
37
|
+
|
|
38
|
+
If you supply your **own** stylesheet instead of a bundled theme, the
|
|
39
|
+
[embedding guide](../orz-markdown-skills/references/embedding.md) lists every CSS
|
|
40
|
+
class the parser emits (semantic containers, tabs/columns, spoilers, KaTeX
|
|
41
|
+
blocks, Mermaid/SMILES/chart canvases, clickable QR codes) and the JavaScript the
|
|
42
|
+
browser runtime needs. Start from `common.css` for the structural rules and layer
|
|
43
|
+
your visual design on top.
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
--line-height: 1.7;
|
|
28
28
|
|
|
29
29
|
--markdown-body-max-width: 800px;
|
|
30
|
-
--markdown-body-padding:
|
|
30
|
+
--markdown-body-padding: 2rem 2rem 4rem;
|
|
31
31
|
--markdown-body-border: none;
|
|
32
32
|
--markdown-body-radius: 0;
|
|
33
33
|
--markdown-body-shadow: none;
|
|
@@ -56,6 +56,8 @@ body {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/* 2. Headings and body copy */
|
|
59
|
+
.markdown-body > :first-child { margin-top: 0; }
|
|
60
|
+
|
|
59
61
|
.markdown-body h1,
|
|
60
62
|
.markdown-body h2,
|
|
61
63
|
.markdown-body h3,
|
|
@@ -556,4 +558,27 @@ body {
|
|
|
556
558
|
box-shadow: none;
|
|
557
559
|
}
|
|
558
560
|
|
|
559
|
-
}
|
|
561
|
+
}
|
|
562
|
+
/* code block — warm paper panel */
|
|
563
|
+
.markdown-body pre { background: #f5f0e6; border: 1px solid #e7ddc9; border-radius: 8px; }
|
|
564
|
+
.markdown-body pre code, .markdown-body pre code.hljs { background: transparent; padding: 0; }
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
/* ============ modern decorative refresh ============ */
|
|
568
|
+
/* blockquote — warm card with a soft corner quote glyph (replaces the top/bottom rules) */
|
|
569
|
+
.markdown-body blockquote { position: relative; border: none; background: #f3ece0; border-radius: 12px;
|
|
570
|
+
padding: 1.1rem 1.4rem 1.1rem 1.6rem; margin: 1.9rem 0; font-style: italic; color: #5a4d42;
|
|
571
|
+
box-shadow: 0 2px 10px rgba(120, 90, 50, 0.06); }
|
|
572
|
+
.markdown-body blockquote::before { content: '“'; position: absolute; left: 0.55rem; top: -0.3rem;
|
|
573
|
+
font-family: Georgia, serif; font-size: 2.6em; line-height: 1; color: #c8a98a; opacity: 0.55; }
|
|
574
|
+
.markdown-body blockquote p { margin: 0 0 0.6em; } .markdown-body blockquote > :last-child { margin-bottom: 0; }
|
|
575
|
+
.markdown-body blockquote blockquote { background: rgba(255, 255, 255, 0.4); box-shadow: none; }
|
|
576
|
+
.markdown-body blockquote blockquote::before { display: none; }
|
|
577
|
+
/* callouts — rounder, softer */
|
|
578
|
+
.markdown-body .info, .markdown-body .success, .markdown-body .warning, .markdown-body .danger { border-radius: 10px; }
|
|
579
|
+
|
|
580
|
+
/* spoiler — add a chevron indicator */
|
|
581
|
+
.markdown-body details.spoil > summary { display: flex; align-items: center; gap: 0.55rem; list-style: none; }
|
|
582
|
+
.markdown-body details.spoil > summary::-webkit-details-marker { display: none; }
|
|
583
|
+
.markdown-body details.spoil > summary::before { content: '▸'; color: #b09a78; transition: transform 0.2s; }
|
|
584
|
+
.markdown-body details.spoil[open] > summary::before { transform: rotate(90deg); }
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
--line-height: 1.8;
|
|
32
32
|
|
|
33
33
|
--markdown-body-max-width: 760px;
|
|
34
|
-
--markdown-body-padding: 5rem 2rem;
|
|
34
|
+
--markdown-body-padding: 2.5rem 2rem 5rem;
|
|
35
35
|
--markdown-body-border: none;
|
|
36
36
|
--markdown-body-radius: 0;
|
|
37
37
|
--markdown-body-shadow: none;
|
|
@@ -59,6 +59,8 @@ body {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/* 2. Headings and body copy */
|
|
62
|
+
.markdown-body > :first-child { margin-top: 0; }
|
|
63
|
+
|
|
62
64
|
.markdown-body h1,
|
|
63
65
|
.markdown-body h2,
|
|
64
66
|
.markdown-body h3,
|
|
@@ -545,7 +547,6 @@ body {
|
|
|
545
547
|
@media (max-width: 640px) {
|
|
546
548
|
.markdown-body {
|
|
547
549
|
padding: 2.5rem 1.25rem;
|
|
548
|
-
font-size: 15px;
|
|
549
550
|
}
|
|
550
551
|
.markdown-body .left {
|
|
551
552
|
float: none;
|
|
@@ -575,4 +576,23 @@ body {
|
|
|
575
576
|
box-shadow: none;
|
|
576
577
|
}
|
|
577
578
|
|
|
578
|
-
}
|
|
579
|
+
}
|
|
580
|
+
/* code block — warm paper panel */
|
|
581
|
+
.markdown-body pre { background: #f6f1e7; border: 1px solid #e6dac3; border-radius: 8px; box-shadow: 0 1px 2px rgba(120,90,50,.06); }
|
|
582
|
+
.markdown-body pre code, .markdown-body pre code.hljs { background: transparent; padding: 0; color: inherit; }
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
/* ============ modern decorative refresh ============ */
|
|
586
|
+
/* blockquote — warm tint with a teal accent edge (pseudo-bar, not a heavy border) */
|
|
587
|
+
.markdown-body blockquote { position: relative; background: #efe7d8; border: none; border-radius: 0 10px 10px 0;
|
|
588
|
+
padding: 0.9rem 1.3rem; margin: 1.9rem 0; font-style: italic; color: var(--text-muted); overflow: hidden; }
|
|
589
|
+
.markdown-body blockquote::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
|
|
590
|
+
background: var(--link-color); }
|
|
591
|
+
.markdown-body blockquote p:last-child { margin-bottom: 0; }
|
|
592
|
+
.markdown-body blockquote blockquote { background: rgba(255, 255, 255, 0.35); }
|
|
593
|
+
|
|
594
|
+
/* spoiler — add a chevron indicator */
|
|
595
|
+
.markdown-body details.spoil > summary { display: flex; align-items: center; gap: 0.55rem; list-style: none; }
|
|
596
|
+
.markdown-body details.spoil > summary::-webkit-details-marker { display: none; }
|
|
597
|
+
.markdown-body details.spoil > summary::before { content: '▸'; color: #9c8a6f; transition: transform 0.2s; }
|
|
598
|
+
.markdown-body details.spoil[open] > summary::before { transform: rotate(90deg); }
|
|
@@ -727,3 +727,14 @@ details.spoil > summary:focus-visible {
|
|
|
727
727
|
page-break-inside: avoid;
|
|
728
728
|
}
|
|
729
729
|
}
|
|
730
|
+
|
|
731
|
+
/* code: uniform dark panel (no highlight.js inner box) */
|
|
732
|
+
.markdown-body pre code, .markdown-body pre code.hljs { background: transparent; padding: 0; }
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
/* ---- mermaid & charts: light panel for legibility on the dark background ---- */
|
|
736
|
+
.markdown-body div.mermaid { background: rgba(255, 255, 255, 0.88); border: 1px solid rgba(255, 255, 255, 0.1);
|
|
737
|
+
border-radius: 10px; padding: 1.1rem; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.22); }
|
|
738
|
+
.markdown-body div.mermaid svg { max-width: 100%; height: auto; }
|
|
739
|
+
.markdown-body canvas.orz-chart { background: rgba(255, 255, 255, 0.88); border-radius: 10px; padding: 0.6rem;
|
|
740
|
+
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.22); max-width: 100%; height: auto; }
|