orz-markdown 1.3.0 → 1.3.2

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 CHANGED
@@ -1,9 +1,17 @@
1
1
  # orz-markdown
2
2
 
3
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21099237.svg)](https://doi.org/10.5281/zenodo.21099237)
4
+
3
5
  A deeply customized markdown-it parser configured with several official plugins and many custom plugins designed to render interactive objects, embedded rich media, and invisible data. Crafted alongside beautifully optimized CSS themes for an excellent out-of-the-box rendering experience. An agent skill is available for using this markdown parser with AI agents.
4
6
 
5
7
  Rendered HTML is intended to live inside a `.markdown-body` container and be paired with one of the bundled themes.
6
8
 
9
+ ## Citation
10
+
11
+ If you use or cite the orz-markdown website or editable document family, please cite the archived release:
12
+
13
+ Yu Wang. (2026). orz-markdown: a Markdown family that travels in one file. Zenodo. https://doi.org/10.5281/zenodo.21099237
14
+
7
15
  ## Installation
8
16
 
9
17
  Install the package via npm:
@@ -55,7 +63,7 @@ Once the runtime is loaded, selecting rendered content and copying it (Cmd/Ctrl-
55
63
 
56
64
  It only transforms selections inside an element with class `markdown-body` (wrap your rendered output in one, e.g. `<article class="markdown-body">…</article>`, or mark a container with `data-orz-copy`), and never touches selections inside `<input>`, `<textarea>`, or `contenteditable` regions.
57
65
 
58
- Generated constructs that lose their source after client-side rendering — `mermaid`, `smiles`, `qrcode`, `youtube` — carry a `data-md` attribute that the walker emits verbatim. As a result a copied table of contents yields its heading links (not `{{toc 2,3}}`) and a copied QR code yields `{{qr ...}}` (not its SVG). **Do not strip `data-md` attributes** if you post-process the HTML.
66
+ Generated constructs that lose their source after client-side rendering — `mermaid`, `smiles`, `qrcode`, `youtube`, `chart` — carry a `data-md` attribute that the walker emits verbatim. As a result a copied table of contents yields its heading links (not `{{toc 2,3}}`) and a copied QR code yields `{{qr ...}}` (not its SVG). **Do not strip `data-md` attributes** if you post-process the HTML.
59
67
 
60
68
  You can also convert a node programmatically:
61
69
 
@@ -130,6 +138,14 @@ node_modules/orz-markdown/orz-markdown-skills/SKILL.md
130
138
 
131
139
  with supporting files under `orz-markdown-skills/references/` and `orz-markdown-skills/assets/`. Point your agent tooling at that directory (e.g. copy or symlink it into your agent's skills folder).
132
140
 
141
+ Or hand your agent the skill straight from the CDN — no install needed:
142
+
143
+ ```
144
+ https://cdn.jsdelivr.net/npm/orz-markdown/orz-markdown-skills/SKILL.md
145
+ ```
146
+
147
+ See **[markdown.orz.how/agents.html](https://markdown.orz.how/agents.html)** for every install route (Claude Code, Claude.ai upload, or point-at-URL for any agent).
148
+
133
149
  ## Official Plugins Bundled
134
150
 
135
151
  This project builds upon `markdown-it` and pre-configures a curated selection of official and popular community plugins.
@@ -159,7 +175,7 @@ We utilize a generalized and uniform `{{plugin_name ...}}` syntax for all custom
159
175
  | **Attributes** | N/A | `# Header {{attrs[id="my-id"]}}`| Injects continuous HTML attributes (classes, IDs, config properties) to the immediately preceding element. |
160
176
  | **Emoji** | `em` | `{{emoji smile}}` / `{{em tada}}` | Renders the specific text alias directly into the corresponding Unicode emoji natively. |
161
177
  | **Table of Content** | N/A | `{{toc}}` or `{{toc 2,3}}` | Autogenerates a Table of Contents based on document headings. Allows specifying bound heading ranges. |
162
- | **Space** | N/A | `{{space 4}}` | Inserts inline horizontal whitespace entities directly into the text. |
178
+ | **Space** | N/A | `{{space 4}}` | Inserts inline horizontal whitespace an empty `inline-block` span sized in `rem` (`{{space 4}}` → 4rem wide). |
163
179
  | **YouTube** | `yt` | `{{youtube dQw4w9WgXcQ}}` | Embedded block element containing a responsive YouTube video iframe matching the ID. |
164
180
  | **Markdown Source**| `md` | `{{md path/to/file.md}}` | Securely embeds the complete content of an external markdown file directly into the parsed structure. |
165
181
  | **QR Code** | `qr` | `{{qr https://example.com}}` | Transforms text inline implicitly into an internally generated SVG QR Code natively. |
package/dist/runtime.js CHANGED
@@ -121,7 +121,7 @@ exports.browserRuntimeScript = String.raw `
121
121
  // ---- copy-as-markdown: DOM -> Markdown walker ---------------------------
122
122
  // Converts a selected DOM fragment back to Markdown so that copying rendered
123
123
  // content yields source, not HTML. Standard constructs are reconstructed from
124
- // their tags; generated constructs (math, mermaid, smiles, qr, youtube) carry
124
+ // their tags; generated constructs (math, mermaid, smiles, qr, youtube, chart) carry
125
125
  // a [data-md] breadcrumb that is emitted verbatim. Written without template
126
126
  // literals/backticks because this whole file is a String.raw template.
127
127
  var BT = String.fromCharCode(96);
@@ -8,7 +8,7 @@ compatibility:
8
8
 
9
9
  # orz-markdown
10
10
 
11
- A deeply customized `markdown-it` instance with 10+ plugins, 9 official plugin bundles, and 12 ready-to-use CSS themes. All rendered HTML lives inside `<article class="markdown-body">`.
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,10 +38,10 @@ 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 10 bundled themes, or `assets/minimal.css`
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. **Three CDN scripts** — Highlight.js, Mermaid.js, SmilesDrawer (loaded in body)
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.
@@ -56,6 +56,7 @@ CDN URLs (pinned versions):
56
56
  | Highlight.js dark CSS | `.../styles/atom-one-dark.min.css` |
57
57
  | Mermaid.js | `https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js` |
58
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` |
59
60
 
60
61
  ### Browser runtime API
61
62
 
@@ -71,7 +72,7 @@ document.body.appendChild(script);
71
72
 
72
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)`.
73
74
 
74
- > **Do not strip `data-md` attributes.** `mermaid`, `smiles`, `qrcode`, and `youtube` 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.
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.
75
76
 
76
77
  ---
77
78
 
@@ -119,7 +120,7 @@ Escape with backslash: `\{{name}}` renders as literal `{{name}}`.
119
120
  |---|---|---|
120
121
  | **span** | `sp` | `{{sp[red] colored}}` · `{{sp[success] ✓ Done}}` |
121
122
  | **emoji** | `em` | `{{emoji wave}}` · `{{em tada}}` |
122
- | **space** | — | `{{space 4}}` → 4 × `&nbsp;` |
123
+ | **space** | — | `{{space 4}}` → 4rem-wide inline-block spacer |
123
124
  | **qrcode** | `qr` | `{{qr https://example.com}}` (click-to-expand SVG) |
124
125
  | **youtube** | `yt` | `{{youtube dQw4w9WgXcQ}}` (responsive iframe) |
125
126
  | **mermaid** | `mm` | `{{mm\ngraph LR\nA-->B\n}}` |
@@ -178,7 +179,7 @@ Documents may carry stable block IDs on headings: `## Title{{attrs[#blk-abc12345
178
179
  |---|---|
179
180
  | `references/syntax.md` | Full syntax with examples for every plugin, container, and extended markdown feature |
180
181
  | `references/block-ids.md` | Stable block-ID preservation rules — REQUIRED reading before editing documents that use `{{attrs[#blk-...]}}` |
181
- | `references/themes.md` | Custom theme guide: design tokens, element checklist, 10 design guidelines |
182
+ | `references/themes.md` | Custom theme guide: design tokens, element checklist, 12 design guidelines |
182
183
  | `references/css-classes.md` | Every CSS class and HTML element the parser emits — the spec for theme authors |
183
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 |
184
185
  | `assets/template.html` | Ready-to-use HTML page template with all scripts and CDN links wired up |
@@ -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-wrapper` | `<div>` | Outer wrapper (for positioning). |
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
- nav.toc, .toc-list {
205
- padding: ...;
206
- border: 1px solid var(--border);
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 |
@@ -166,14 +166,15 @@ when) all of this holds:
166
166
  2. **Content is inside `.markdown-body`** (or an element with `data-orz-copy`).
167
167
  The handler ignores selections elsewhere and inside inputs/textareas.
168
168
  3. **`data-md` breadcrumbs are preserved.** Generated constructs whose source is
169
- otherwise lost — `mermaid`, `smiles`, `qrcode`, `youtube`, `toc`, `chart`,
170
- `{{sp}}` (and any plugin that adds one) — carry `data-md` with their original
171
- directive. The walker emits it verbatim. **Never strip `data-md`** when you
172
- post-process HTML.
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.
173
172
  4. **Plugin/container classes are preserved.** Constructs without a `data-md`
174
- are recovered *by class*: `<span class="red">` → `{{sp[red] …}}`,
173
+ are recovered *by class/structure*: `<span class="red">` → `{{sp[red] …}}`,
175
174
  `<div class="center">` → `::: center … :::`, nested `cols`/`tabs` get the
176
- right fence length. If you rewrite/strip these classes, copy loses the source.
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.
177
178
 
178
179
  Caveat: a container (`::: center`, `:::: cols`, spoiler, tabs) is recovered only
179
180
  when the **selection includes the container element**, not just the text inside
@@ -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}} → &nbsp;&nbsp;&nbsp;&nbsp; (4 non-breaking spaces)
314
- {{space 1}} → &nbsp;
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 `<nav class="toc">` from all headings in the document. Optional argument is a comma-separated level range (`min,max`), defaulting to all levels. Must appear after the headings it references in the source (or at the start the parser makes a pass over the full document).
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: `nav.toc`, `.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
- - [ ] `nav.toc` `.toc-list` — list reset, link styles
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,6 +1,6 @@
1
1
  {
2
2
  "name": "orz-markdown",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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",
package/themes/common.css CHANGED
@@ -10,12 +10,19 @@
10
10
 
11
11
  .markdown-body img {
12
12
  display: block;
13
- width: auto;
14
13
  max-width: 100%;
15
14
  max-height: 100%;
16
- height: auto;
17
15
  object-fit: contain;
18
16
  }
17
+ /* Responsive by default, but explicit dimensions from `![](img =200x100)`
18
+ (markdown-it-imsize → width/height attributes) must win: only auto-size
19
+ images that DON'T carry an explicit width/height. */
20
+ .markdown-body img:not([width]) {
21
+ width: auto;
22
+ }
23
+ .markdown-body img:not([height]) {
24
+ height: auto;
25
+ }
19
26
 
20
27
  .markdown-body p > img {
21
28
  display: inline;