@haklex/rich-litexml 0.15.2 → 0.15.4
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/package.json +2 -3
- package/.claude/skills/litexml-authoring/SKILL.md +0 -86
- package/.claude/skills/litexml-authoring/references/authoring-recipes.md +0 -141
- package/.claude/skills/litexml-authoring/references/cli.md +0 -166
- package/.claude/skills/litexml-authoring/references/nodes-extensions.md +0 -399
- package/.claude/skills/litexml-authoring/references/nodes-structural.md +0 -150
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haklex/rich-litexml",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4",
|
|
4
4
|
"description": "Bidirectional Lexical SerializedNode <-> XML conversion with plugin registry",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"main": "./dist/node.mjs",
|
|
29
29
|
"files": [
|
|
30
|
-
"dist"
|
|
31
|
-
".claude/skills/litexml-authoring/**"
|
|
30
|
+
"dist"
|
|
32
31
|
],
|
|
33
32
|
"dependencies": {
|
|
34
33
|
"lexical": "^0.44.0",
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: litexml-authoring
|
|
3
|
-
description: Use when writing Haklex articles with Markdown plus LiteXML, choosing the appropriate Haklex node for content, authoring LiteXML fragments, or converting a LightXML/LiteXML string or file into Lexical SerializedEditorState JSON, Markdown, or Static Render HTML via the `litexml` CLI.
|
|
4
|
-
user_invocable: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# LiteXML Authoring
|
|
8
|
-
|
|
9
|
-
Use this skill when an article needs Haklex-specific rich nodes, or when the user asks to convert LiteXML to Lexical JSON / Markdown / HTML via the `litexml` CLI.
|
|
10
|
-
|
|
11
|
-
The skill is split into a thin index (this file) plus per-topic references. Load the matching reference when you start the actual work — the index is intentionally short so the format decision table and node selection map fit in one read.
|
|
12
|
-
|
|
13
|
-
## References
|
|
14
|
-
|
|
15
|
-
| File | When to load |
|
|
16
|
-
| ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
-
| [`references/cli.md`](./references/cli.md) | Running the `litexml` binary — every flag, every input/output mode, every runtime caveat. |
|
|
18
|
-
| [`references/nodes-structural.md`](./references/nodes-structural.md) | Authoring with structural tags (`<p>`, headings, lists, tables, links) and inline formatting (`<b>`, `<em>`, `<code>`, …). |
|
|
19
|
-
| [`references/nodes-extensions.md`](./references/nodes-extensions.md) | Authoring with Haklex extension tags (media, code, math, callouts, containers, footnotes, chat, poll, …). |
|
|
20
|
-
| [`references/authoring-recipes.md`](./references/authoring-recipes.md) | CDATA usage, block IDs, nested-content rules, similar-node disambiguation, validation, adding new nodes. |
|
|
21
|
-
|
|
22
|
-
`packages/rich-editor/docs/markdown-flavor-litexml.md` is the canonical tag contract — read it before changing any tag name or attribute schema.
|
|
23
|
-
|
|
24
|
-
## Format decision
|
|
25
|
-
|
|
26
|
-
| Content requirement | Use |
|
|
27
|
-
| ----------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
28
|
-
| Plain prose, headings, simple links, lists, quotes, tables, code fences | **Markdown** |
|
|
29
|
-
| Any Haklex extension tag is required | **LiteXML** — for the whole fragment, including surrounding prose |
|
|
30
|
-
| A block needs stable identity for later edits | LiteXML with `id="..."` (maps to `$.blockId`) |
|
|
31
|
-
| Fresh poll/chat content | LiteXML without IDs — the reader mints them |
|
|
32
|
-
|
|
33
|
-
Once a fragment contains any LiteXML tag, **all** surrounding prose in that fragment must also be LiteXML. Markdown is not parsed inside a LiteXML fragment.
|
|
34
|
-
|
|
35
|
-
## CLI quick start
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# inside the haklex monorepo
|
|
39
|
-
pnpm --silent litexml input.xml --format json --compact # → SerializedEditorState
|
|
40
|
-
pnpm --silent litexml input.xml --format markdown # → Markdown
|
|
41
|
-
pnpm --silent litexml input.xml --format html --open # → HTML preview, browser
|
|
42
|
-
|
|
43
|
-
# outside the monorepo
|
|
44
|
-
npx --yes -p @haklex/rich-litexml-cli litexml input.xml --format json
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Full flag reference, output forms, theme/variant/lang options, error modes, validation pipelines: [`references/cli.md`](./references/cli.md).
|
|
48
|
-
|
|
49
|
-
## Node selection map
|
|
50
|
-
|
|
51
|
-
Detailed `When` / `Avoid when` / params / body rules for each tag live in [`nodes-structural.md`](./references/nodes-structural.md) and [`nodes-extensions.md`](./references/nodes-extensions.md). Use this table only to decide which reference to open.
|
|
52
|
-
|
|
53
|
-
| Need | Tag | Reference |
|
|
54
|
-
| ------------------------------------------------------ | --------------------------------------------------------------------------- | ---------- |
|
|
55
|
-
| Paragraph, heading, list, table, link, inline format | `<p>` / `<h*>` / `<ul>` / `<ol>` / `<table>` / `<a>` / `<b>` / `<code>` / … | structural |
|
|
56
|
-
| Quote (with optional `attribution`) | `<blockquote>` | structural |
|
|
57
|
-
| Single image / multi-image / video / embed / link card | `<img>` / `<gallery>` / `<video>` / `<embed>` / `<link-card>` | extensions |
|
|
58
|
-
| Code (single / multi-file) | `<codeblock>` / `<code-snippet>` | extensions |
|
|
59
|
-
| Diagram (Mermaid / opaque) | `<mermaid>` / `<excalidraw>` | extensions |
|
|
60
|
-
| Math (inline / block) | `<math>` / `<math display="block">` | extensions |
|
|
61
|
-
| Callout (semantic / page-wide) | `<alert>` / `<banner>` | extensions |
|
|
62
|
-
| Collapsible / nested doc / multi-column | `<details>` / `<nested-doc>` / `<grid><cell>` | extensions |
|
|
63
|
-
| Inline annotation | `<spoiler>` / `<ruby>` / `<mention>` / `<tag>` / `<comment>` | extensions |
|
|
64
|
-
| Footnote | `<footnote>` + `<footnote-section>` | extensions |
|
|
65
|
-
| Chat / poll | `<chat>` / `<poll>` | extensions |
|
|
66
|
-
| Internal review marker | `<agent-diff>` | extensions |
|
|
67
|
-
|
|
68
|
-
## Cross-cutting rules
|
|
69
|
-
|
|
70
|
-
- Canonical lowercase tag names. Do **not** emit legacy aliases (`<linkcard>`, `<codesnippet>`, `<nesteddoc>`, `<code-block>`).
|
|
71
|
-
- Quote every attribute value.
|
|
72
|
-
- Escape XML-sensitive text (`&`, `<`, `>`, `"`). Use CDATA for opaque JSON, drawing snapshots, and multi-line code bodies.
|
|
73
|
-
- `<alert>`, `<banner>`, `<nested-doc>`, `<grid><cell>` hold a **fresh nested editor state** — wrap their body in block tags (`<p>`, `<h*>`, …), not bare text.
|
|
74
|
-
- A fragment that mixes any extension tag with prose must be wholly LiteXML; Markdown syntax is not parsed inside.
|
|
75
|
-
|
|
76
|
-
Details, gotchas, and the full disambiguation matrix: [`references/authoring-recipes.md`](./references/authoring-recipes.md).
|
|
77
|
-
|
|
78
|
-
## Validation
|
|
79
|
-
|
|
80
|
-
After producing or editing LiteXML, round-trip to compact JSON to confirm the registry parses every tag:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
pnpm --silent litexml '<doc><p>Hello</p></doc>' --format json --compact
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
If the command succeeds but a downstream editor cannot materialize a node, the runtime is missing that Haklex node class — re-check the consumer's `nodes` registration, not the CLI.
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# Authoring recipes and gotchas
|
|
2
|
-
|
|
3
|
-
Recurring patterns that come up when writing LiteXML fragments. Each entry is intentionally small — pull the matching one when you hit the situation.
|
|
4
|
-
|
|
5
|
-
## When to use CDATA
|
|
6
|
-
|
|
7
|
-
Wrap body content in `<![CDATA[ ... ]]>` whenever the body is opaque or contains characters that XML would otherwise escape:
|
|
8
|
-
|
|
9
|
-
| Situation | Required? |
|
|
10
|
-
| ------------------------------------------------ | ------------------------------------------------------------------- |
|
|
11
|
-
| `<excalidraw>` JSON snapshot | yes |
|
|
12
|
-
| `<codeblock>` containing `<`, `>`, `&`, or `]]>` | yes |
|
|
13
|
-
| `<codeblock>` containing only safe characters | optional, but preferred for multi-line code |
|
|
14
|
-
| `<file>` body inside `<code-snippet>` | same rules as `<codeblock>` |
|
|
15
|
-
| `<mermaid>` source | optional; usually unnecessary because Mermaid syntax is XML-safe |
|
|
16
|
-
| `<math>` equation | rarely needed; KaTeX source uses `\`, not XML metacharacters |
|
|
17
|
-
| `<p>`, `<h1>`, `<li>`, `<td>` prose | never — escape entities instead (`&`, `<`, `>`, `"`) |
|
|
18
|
-
|
|
19
|
-
CDATA inside `<codeblock>` and `<file>` is parsed via a small linkedom-specific extraction (`extractCdataText`). Do **not** nest CDATA sections or place trailing `]]>` inside the body.
|
|
20
|
-
|
|
21
|
-
## Block IDs (`id` attribute)
|
|
22
|
-
|
|
23
|
-
- **Omit** for fresh content. The hydrating editor generates an ID on insert.
|
|
24
|
-
- **Add** when later tool calls need to target the exact block, or when porting an existing document that already carries IDs (so cross-references / diff entries stay attached).
|
|
25
|
-
- The `id` attribute maps to `$.blockId`. It is preserved on round-trip (`litexml ... --format json` then back).
|
|
26
|
-
- Tags that accept `id`: every block-level tag in [`nodes-structural.md`](./nodes-structural.md) and [`nodes-extensions.md`](./nodes-extensions.md) except inline tags and `<tr>` / `<th>` / `<td>`.
|
|
27
|
-
|
|
28
|
-
## Quote attribution
|
|
29
|
-
|
|
30
|
-
`<blockquote>` accepts an optional `attribution` attribute. The reader emits `type: "rich-quote"` when present and `type: "quote"` when absent — both materialize as the same `RichQuoteNode` in the editor.
|
|
31
|
-
|
|
32
|
-
```xml
|
|
33
|
-
<blockquote attribution="— Wang Xizhi, Lantingji Xu">
|
|
34
|
-
<p>未尝不临文嗟悼,不能喻之于怀。</p>
|
|
35
|
-
</blockquote>
|
|
36
|
-
|
|
37
|
-
<blockquote>
|
|
38
|
-
<p>An unattributed quote.</p>
|
|
39
|
-
</blockquote>
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Nested block content vs inline content
|
|
43
|
-
|
|
44
|
-
Containers that hold a fresh `SerializedEditorState` (and therefore need block children, not inline text):
|
|
45
|
-
|
|
46
|
-
- `<alert>`
|
|
47
|
-
- `<banner>`
|
|
48
|
-
- `<nested-doc>`
|
|
49
|
-
- `<grid><cell>`
|
|
50
|
-
|
|
51
|
-
Always wrap the body in block tags (`<p>`, `<h2>`, `<ul>`, etc.):
|
|
52
|
-
|
|
53
|
-
```xml
|
|
54
|
-
<alert type="tip">
|
|
55
|
-
<p>Wrap the body in a paragraph.</p>
|
|
56
|
-
</alert>
|
|
57
|
-
|
|
58
|
-
<!-- bad: bare text body -->
|
|
59
|
-
<alert type="tip">Bare text — works at parse time but won't round-trip cleanly.</alert>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
`<details>` is different: it uses ordinary block children (no nested editor state). Either form is fine, but use `<p>` for consistency.
|
|
63
|
-
|
|
64
|
-
## Mixing extension tags with prose
|
|
65
|
-
|
|
66
|
-
If a fragment contains **any** LiteXML extension tag, the surrounding prose must also be expressed as LiteXML — Markdown is not parsed inside a LiteXML fragment.
|
|
67
|
-
|
|
68
|
-
```xml
|
|
69
|
-
<!-- correct -->
|
|
70
|
-
<h2>Install</h2>
|
|
71
|
-
<p>Run the command:</p>
|
|
72
|
-
<codeblock lang="bash">pnpm add @haklex/rich-editor</codeblock>
|
|
73
|
-
<p>Then import the editor.</p>
|
|
74
|
-
|
|
75
|
-
<!-- wrong: Markdown syntax inside a LiteXML fragment is rendered literally -->
|
|
76
|
-
## Install
|
|
77
|
-
Run the command:
|
|
78
|
-
<codeblock lang="bash">pnpm add @haklex/rich-editor</codeblock>
|
|
79
|
-
Then import the editor.
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
The right time to write pure Markdown is when the whole document has no extension tags. As soon as one shows up, lift the whole document into LiteXML.
|
|
83
|
-
|
|
84
|
-
## Choosing similar nodes
|
|
85
|
-
|
|
86
|
-
| Decision | Use | Not |
|
|
87
|
-
| ---------------------------------------- | --------------------------------- | --------------------------------------------- |
|
|
88
|
-
| Quoted external text | `<blockquote>` | `<alert>` (use for warnings/tips, not quotes) |
|
|
89
|
-
| Editorial warning | `<alert type="warning">` | `<blockquote>` |
|
|
90
|
-
| Page-wide announcement | `<banner>` | `<alert>` |
|
|
91
|
-
| Multi-column comparison data | `<table>` | `<grid>` |
|
|
92
|
-
| Multi-column editorial layout | `<grid>` | `<table>` |
|
|
93
|
-
| Inline citation | `<a>` | `<link-card>` |
|
|
94
|
-
| Standalone reference deserving a preview | `<link-card>` | `<a>` |
|
|
95
|
-
| YouTube / Vimeo embed | `<embed url="..." source="..."/>` | `<img>` (which would be a static thumbnail) |
|
|
96
|
-
| Direct video asset | `<video src="..."/>` | `<embed>` |
|
|
97
|
-
| Single image | `<img>` | `<gallery>` |
|
|
98
|
-
| Image set | `<gallery>` | many `<img>` |
|
|
99
|
-
| Single-language code | `<codeblock>` | `<code-snippet>` |
|
|
100
|
-
| Multi-file example | `<code-snippet>` | repeated `<codeblock>` |
|
|
101
|
-
| Mermaid-expressible diagram | `<mermaid>` | `<excalidraw>` |
|
|
102
|
-
| Opaque hand-drawn snapshot | `<excalidraw>` | `<mermaid>` |
|
|
103
|
-
| Inline equation | `<math>` | `<math display="block">` |
|
|
104
|
-
| Standalone equation | `<math display="block">` | inline `<math>` |
|
|
105
|
-
| Collapsible secondary content | `<details>` | `<spoiler>` (inline only) |
|
|
106
|
-
| Hidden inline reveal | `<spoiler>` | `<details>` |
|
|
107
|
-
| Reader-visible callout | `<alert>` / `<banner>` | `<comment>` |
|
|
108
|
-
| Reviewer note (not for end readers) | `<comment>` | `<alert>` |
|
|
109
|
-
| Survey / live vote | `<poll>` | `<ul>` / `<ol>` |
|
|
110
|
-
| Static option list | `<ul>` | `<poll>` |
|
|
111
|
-
| Task checklist | `<ul type="check">` | `<poll>` |
|
|
112
|
-
| Transcript | `<chat>` | `<blockquote>` |
|
|
113
|
-
|
|
114
|
-
## Validation
|
|
115
|
-
|
|
116
|
-
After producing or editing a LiteXML fragment, convert to compact JSON to confirm the registry parses every tag:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
pnpm --silent litexml '<doc><p>Hello</p></doc>' --format json --compact
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
For round-trip verification on an article file:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
litexml article.xml --format json -o /tmp/state.json
|
|
126
|
-
litexml /tmp/state.json --format markdown -o /tmp/article.md
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Any node that disappears or changes shape across the round-trip is a writer/reader bug — file against `@haklex/rich-litexml`, not the CLI.
|
|
130
|
-
|
|
131
|
-
When `litexml` succeeds but a downstream editor cannot materialize a node, the consumer is missing that Haklex node class. The CLI registry is authoritative for what _can_ be parsed; node class registration is the consumer's responsibility.
|
|
132
|
-
|
|
133
|
-
## Adding a new node
|
|
134
|
-
|
|
135
|
-
If you find yourself wanting a tag that this skill does not document, the underlying machinery is in `packages/rich-litexml/`. Following its `CLAUDE.md`:
|
|
136
|
-
|
|
137
|
-
1. Add a writer in `packages/rich-litexml/src/writers/` (SerializedNode JSON → XML).
|
|
138
|
-
2. Add a reader in `packages/rich-litexml/src/readers/` (XML → SerializedNode JSON).
|
|
139
|
-
3. Register both in `createDefaultRegistry()` in `src/default-registry.ts`.
|
|
140
|
-
4. Add a roundtrip test in `tests/`.
|
|
141
|
-
5. Update this skill's [`nodes-extensions.md`](./nodes-extensions.md) so the new tag is discoverable from the authoring side.
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# litexml CLI reference
|
|
2
|
-
|
|
3
|
-
`@haklex/rich-litexml-cli` publishes the `litexml` binary. It accepts **LiteXML** or **Lexical SerializedEditorState JSON** as input (auto-detected) and emits one of three target formats: **HTML preview**, **Lexical JSON**, or **Markdown**.
|
|
4
|
-
|
|
5
|
-
This file is the canonical reference for every flag, every input/output mode, and the runtime caveats. The main `SKILL.md` only lists the format decision matrix; come here when you need the actual command.
|
|
6
|
-
|
|
7
|
-
## Invocation forms
|
|
8
|
-
|
|
9
|
-
### Inside this monorepo
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
pnpm --silent litexml INPUT --format FMT [options]
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
The repo's root `package.json` exposes `litexml` as a pnpm-runnable script that resolves the local workspace build. Always pair with `--silent` so pnpm chatter does not corrupt stdout when piping into a file.
|
|
16
|
-
|
|
17
|
-
### Outside the repo
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npx --yes -p @haklex/rich-litexml-cli@VERSION litexml INPUT --format FMT [options]
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Or after a global install:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install -g @haklex/rich-litexml-cli
|
|
27
|
-
litexml INPUT --format FMT [options]
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
`INPUT` is a file path, `-`, or an inline LiteXML / JSON string; `FMT` is one of `html | json | markdown`; `VERSION` is the published version tag. See the next sections for the concrete forms.
|
|
31
|
-
|
|
32
|
-
The package depends on `@haklex/rich-compose`, `@haklex/rich-headless`, and `@haklex/rich-litexml` at the same version, and reads `@haklex/rich-compose/dist/style.css` plus `dist/litexml-html-preview-client.js` at runtime via `require.resolve`. Do not strip those packages in a custom install.
|
|
33
|
-
|
|
34
|
-
## Input
|
|
35
|
-
|
|
36
|
-
| Form | Example | Notes |
|
|
37
|
-
| ------------------ | -------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
38
|
-
| File path | `litexml article.xml --format json` | Path is detected via `existsSync`. Reads with `utf8`. |
|
|
39
|
-
| Stdin | `cat article.xml \| litexml - --format json` | Explicit `-` reads from `fd 0`. Also used when input arg is omitted entirely. |
|
|
40
|
-
| Inline string | `litexml '<p>hi</p>' --format json` | If the arg does not match an existing file and starts with `<` or `{`, it is parsed directly. |
|
|
41
|
-
| Force input format | `litexml input.txt --input-format litexml` | Use when auto-detection cannot decide (e.g. unusual whitespace or empty leading lines). |
|
|
42
|
-
|
|
43
|
-
**Auto-detection rule** (`src/input.ts`): first non-whitespace character — `<` → LiteXML, `{` → JSON, anything else → error. Override with `--input-format litexml|json` (alias `-i`).
|
|
44
|
-
|
|
45
|
-
## Output
|
|
46
|
-
|
|
47
|
-
| Form | Example |
|
|
48
|
-
| ------------------ | ----------------------------------------------------- |
|
|
49
|
-
| Stdout (default) | `litexml input.xml --format html > article.html` |
|
|
50
|
-
| Explicit file | `litexml input.xml --format html -o article.html` |
|
|
51
|
-
| Browser preview | `litexml input.xml --format html --open` |
|
|
52
|
-
| Write **and** open | `litexml input.xml --format html -o page.html --open` |
|
|
53
|
-
|
|
54
|
-
When `--open` is set without `-o`, the CLI writes the HTML to a tmp file (`mkdtemp` under `os.tmpdir()`) and opens it via `open` / `cmd /c start` / `xdg-open` depending on platform.
|
|
55
|
-
|
|
56
|
-
Stdout EPIPE is handled gracefully (`process.stdout.on('error', ...)`), so piping into `head` / `less` will not crash the process.
|
|
57
|
-
|
|
58
|
-
## Flags
|
|
59
|
-
|
|
60
|
-
| Flag | Alias | Applies to | Default | Meaning |
|
|
61
|
-
| ----------------------- | ----- | ----------- | ------------ | ----------------------------------------------------------------------------- |
|
|
62
|
-
| `--format <fmt>` | `-f` | all | **required** | `html` \| `json` \| `markdown`. |
|
|
63
|
-
| `--input-format <fmt>` | `-i` | all | auto | Force `litexml` or `json`. |
|
|
64
|
-
| `--output <file>` | `-o` | all | stdout | Write rendered output to a file. |
|
|
65
|
-
| `--compact` | | `json` only | off | Strip indentation from emitted JSON. |
|
|
66
|
-
| `--theme <light\|dark>` | | `html` only | `light` | Sets `<meta color-scheme>` and `<html>` background. |
|
|
67
|
-
| `--variant <v>` | | `html` only | `article` | `article` (sans, 16px) \| `note` (CJK serif, 16px) \| `comment` (sans, 14px). |
|
|
68
|
-
| `--title <t>` | | `html` only | derived | Sets the HTML `<title>`. Default: input filename or `Haklex LiteXML Preview`. |
|
|
69
|
-
| `--lang <l>` | | `html` only | `en` | Sets `<html lang>`. |
|
|
70
|
-
| `--open` | | `html` only | off | Open the generated HTML in the system browser. |
|
|
71
|
-
| `--help` | `-h` | — | — | Print help and exit `0`. |
|
|
72
|
-
|
|
73
|
-
Irrelevant flags are not errors — the CLI prints a warning on stderr (`Warning: ignoring options not applicable to --format <fmt>: ...`) and continues. Use `2>/dev/null` to suppress.
|
|
74
|
-
|
|
75
|
-
## Output formats in detail
|
|
76
|
-
|
|
77
|
-
### `--format json` — Lexical SerializedEditorState
|
|
78
|
-
|
|
79
|
-
Emits a full `SerializedEditorState` (i.e. `{ "root": { ... } }`), not a node array. Use this when you need to feed the result into `editor.parseEditorState(JSON.stringify(state))` or persist it to a database.
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
litexml input.xml --format json > state.json
|
|
83
|
-
litexml input.xml --format json --compact # single-line, smallest
|
|
84
|
-
litexml input.xml --format json -o state.json
|
|
85
|
-
litexml '<p>hi</p>' --format json --compact # inline → JSON, one-shot
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
When the input is already JSON, the CLI still rebuilds it via `JSON.parse` → format → emit. This is intentional: it lets you re-format / compact existing state files via the same binary.
|
|
89
|
-
|
|
90
|
-
### `--format markdown` — `$toMarkdown()` via rich-headless
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
litexml input.xml --format markdown > article.md
|
|
94
|
-
litexml state.json --format markdown # auto-detected as JSON
|
|
95
|
-
litexml state.json --format markdown -i json # force
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
The Markdown writer is `@haklex/rich-headless`'s `$toMarkdown()`. Coverage includes all standard nodes plus haklex extensions (mentions, footnotes, KaTeX, code blocks, tables, banners, alerts, spoilers, etc.). When a Haklex node has no Markdown analogue, the headless writer emits a sensible textual fallback rather than failing.
|
|
99
|
-
|
|
100
|
-
Useful pairings:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
# Round-trip: render the canonical Markdown that an article would export as
|
|
104
|
-
litexml article.xml --format markdown | diff - article.md
|
|
105
|
-
|
|
106
|
-
# Convert a stored Lexical state back into editable Markdown
|
|
107
|
-
litexml state.json --format markdown -o editable.md
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### `--format html` — full preview document
|
|
111
|
-
|
|
112
|
-
Writes a standalone HTML document with:
|
|
113
|
-
|
|
114
|
-
- `@haklex/rich-compose/dist/style.css` inlined inside `<style>`.
|
|
115
|
-
- The `SerializedEditorState` embedded as `<script id="haklex-litexml-payload" type="application/json">`.
|
|
116
|
-
- `@haklex/rich-compose/dist/litexml-html-preview-client.js` inlined inside a trailing `<script>`. The bundle reads the payload script and renders into `#haklex-litexml-root` via the same Rich Compose renderer used by the static renderer.
|
|
117
|
-
|
|
118
|
-
The resulting file is fully self-contained — no external network requests, no missing CSS — and can be opened from any disk path. Theme / variant / title / lang are baked into the document at render time.
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
litexml input.xml --format html > preview.html
|
|
122
|
-
litexml input.xml --format html -o preview.html
|
|
123
|
-
litexml input.xml --format html --open # tmpfile + system open
|
|
124
|
-
litexml input.xml --format html --theme dark -o dark.html
|
|
125
|
-
litexml input.xml --format html --variant note --lang ja -o note.html
|
|
126
|
-
litexml input.xml --format html --title "Draft v3" -o draft.html
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
If you build the CLI from source and `@haklex/rich-compose` has not been built, the HTML format will fail with `Cannot resolve @haklex/rich-compose asset "style.css"`. Run `pnpm --filter @haklex/rich-compose build` first.
|
|
130
|
-
|
|
131
|
-
## Exit behavior
|
|
132
|
-
|
|
133
|
-
- Success: exit code `0`. Output goes to stdout (or `-o` path).
|
|
134
|
-
- Error: exit code `1`, message + full `HELP` text written to stderr. Common causes: missing `--format`, unknown flag, empty input, malformed JSON, unresolvable compose asset.
|
|
135
|
-
- `--help`: exit code `0`, help text written to stdout.
|
|
136
|
-
- EPIPE on stdout (e.g. piping into `head`): exit code `0` silently.
|
|
137
|
-
|
|
138
|
-
## Validation recipe
|
|
139
|
-
|
|
140
|
-
After producing or editing a LiteXML fragment, convert to compact JSON to confirm the registry parses every tag:
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
pnpm --silent litexml '<doc><p>Hello</p></doc>' --format json --compact
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
If the command succeeds but a downstream editor cannot materialize a node, the runtime is missing that Haklex node class — re-check the consumer's `nodes` registration, not the CLI.
|
|
147
|
-
|
|
148
|
-
For round-trip verification on a real article:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
litexml article.xml --format json | litexml - --format markdown -i json
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Any node that round-trips differently between LiteXML → JSON → Markdown is a writer/reader bug in `@haklex/rich-litexml` or a missing Markdown export in `@haklex/rich-headless`.
|
|
155
|
-
|
|
156
|
-
## Common mistakes
|
|
157
|
-
|
|
158
|
-
| Mistake | Symptom | Fix |
|
|
159
|
-
| ------------------------------------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
160
|
-
| Omitting `--format` | `Missing required option: --format <html\|json\|markdown>.` | Always supply `-f` / `--format`. |
|
|
161
|
-
| Forgetting `--silent` when piping | pnpm progress lines mixed into the output file | `pnpm --silent litexml ... > out.html`. |
|
|
162
|
-
| Passing inline LiteXML without quotes | Shell parses `<` as redirect | Wrap in single quotes: `litexml '<p>x</p>' -f json`. |
|
|
163
|
-
| Using `--compact` with `-f html` | Warning, no effect | `--compact` is JSON-only. |
|
|
164
|
-
| Running `-f html` without compose build | `Cannot resolve @haklex/rich-compose asset "style.css".` | Run `pnpm --filter @haklex/rich-compose build` (only inside a source checkout). |
|
|
165
|
-
| Treating output as node array | Downstream `editor.parseEditorState` rejects the JSON | Output is `SerializedEditorState` (`{root: {...}}`), not a node list. |
|
|
166
|
-
| Mixing Markdown syntax inside a LiteXML fragment | `**bold**` rendered literally | If the fragment contains any LiteXML tag, write the whole surrounding prose as LiteXML. |
|
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
# Haklex extension nodes
|
|
2
|
-
|
|
3
|
-
These tags are not part of CommonMark. Each one maps to a Haklex-specific Lexical node and is the only way to author content that those node renderers can pick up. For prose scaffolding (paragraphs, lists, tables, links, inline formatting) see [`nodes-structural.md`](./nodes-structural.md).
|
|
4
|
-
|
|
5
|
-
Every block-level extension may carry `id="..."` → `$.blockId`. Omit for fresh authoring; preserve when editing.
|
|
6
|
-
|
|
7
|
-
## Media
|
|
8
|
-
|
|
9
|
-
### `<img>` — image
|
|
10
|
-
|
|
11
|
-
- **When**: A single image, optionally with caption and explicit dimensions.
|
|
12
|
-
- **Avoid when**: Multiple related images — use `<gallery>`. Decorative inline glyph — use the renderer's icon system.
|
|
13
|
-
- **Required**: `src`.
|
|
14
|
-
- **Optional**: `id`, `alt`, `width` (numeric), `height` (numeric), `caption`, `thumbhash`, `accent` (color string for image rendering).
|
|
15
|
-
- **Body**: self-closing.
|
|
16
|
-
- **Inside `<gallery>`**: `<img>` becomes a gallery image item instead of a standalone image node — only `src` and `alt` are read.
|
|
17
|
-
|
|
18
|
-
```xml
|
|
19
|
-
<img src="/photo.jpg" alt="Cover" width="1200" height="800" caption="At dusk." accent="#ff8855" />
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
### `<video>` — video
|
|
23
|
-
|
|
24
|
-
- **When**: A directly playable video asset.
|
|
25
|
-
- **Avoid when**: Provider-hosted clip (YouTube, Vimeo) — use `<embed>`.
|
|
26
|
-
- **Required**: `src`.
|
|
27
|
-
- **Optional**: `id`, `poster` (preview image URL), `width`, `height`.
|
|
28
|
-
- **Body**: self-closing.
|
|
29
|
-
|
|
30
|
-
```xml
|
|
31
|
-
<video src="/clip.mp4" poster="/thumb.jpg" />
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### `<link-card>` — rich link preview
|
|
35
|
-
|
|
36
|
-
- **When**: A standalone reference URL that deserves a visible card (title, description, favicon, hero image).
|
|
37
|
-
- **Avoid when**: Inline citation in the middle of prose — use `<a>`.
|
|
38
|
-
- **Required**: `url`.
|
|
39
|
-
- **Optional**: `id`, `source`, `title`, `description`, `favicon`, `image`.
|
|
40
|
-
- **Body**: self-closing.
|
|
41
|
-
|
|
42
|
-
```xml
|
|
43
|
-
<link-card url="https://haklex.dev"
|
|
44
|
-
title="Haklex"
|
|
45
|
-
description="Lexical-based rich editor ecosystem."
|
|
46
|
-
favicon="https://haklex.dev/icon.svg" />
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### `<embed>` — provider embed
|
|
50
|
-
|
|
51
|
-
- **When**: A third-party provider embed such as YouTube, Vimeo, CodePen, Tweet, etc. The renderer detects the provider and produces the right iframe / oEmbed payload.
|
|
52
|
-
- **Avoid when**: A static screenshot — use `<img>`.
|
|
53
|
-
- **Required**: `url`.
|
|
54
|
-
- **Optional**: `id`, `source` (provider hint, e.g. `youtube`, `vimeo`).
|
|
55
|
-
- **Body**: self-closing.
|
|
56
|
-
|
|
57
|
-
```xml
|
|
58
|
-
<embed url="https://www.youtube.com/watch?v=dQw4w9WgXcQ" source="youtube" />
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### `<gallery>` — image gallery
|
|
62
|
-
|
|
63
|
-
- **When**: A related image set displayed together.
|
|
64
|
-
- **Avoid when**: A single figure — use `<img>`.
|
|
65
|
-
- **Required**: one or more `<img>` children.
|
|
66
|
-
- **Optional**: `id`, `layout` (defaults to `grid`).
|
|
67
|
-
- **Body**: only `<img src="..." alt="..." />` children. Other tags are ignored.
|
|
68
|
-
|
|
69
|
-
```xml
|
|
70
|
-
<gallery layout="grid">
|
|
71
|
-
<img src="/a.jpg" alt="A" />
|
|
72
|
-
<img src="/b.jpg" alt="B" />
|
|
73
|
-
<img src="/c.jpg" alt="C" />
|
|
74
|
-
</gallery>
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Code
|
|
78
|
-
|
|
79
|
-
### `<codeblock>` — single code listing
|
|
80
|
-
|
|
81
|
-
- **When**: One code listing in a single language.
|
|
82
|
-
- **Avoid when**: Multi-file example (e.g. `index.ts` + `package.json`) — use `<code-snippet>`. Short inline phrase — use `<code>`.
|
|
83
|
-
- **Required**: none (empty body is allowed).
|
|
84
|
-
- **Optional**: `id`, `lang`.
|
|
85
|
-
- **Body**: raw code text. For shebangs, imports, template strings, XML-sensitive characters (`<`, `&`), or multi-line code, prefer `<![CDATA[...]]>` so the body is preserved verbatim.
|
|
86
|
-
|
|
87
|
-
```xml
|
|
88
|
-
<codeblock lang="ts"><![CDATA[
|
|
89
|
-
import { foo } from './bar';
|
|
90
|
-
const x = <T>(t: T): T => t;
|
|
91
|
-
]]></codeblock>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Note: the canonical tag is `<codeblock>`, **not** `<code-block>`.
|
|
95
|
-
|
|
96
|
-
### `<code-snippet>` — multi-file code example
|
|
97
|
-
|
|
98
|
-
- **When**: An example that spans multiple named files.
|
|
99
|
-
- **Avoid when**: Single file — use `<codeblock>`.
|
|
100
|
-
- **Required**: one or more `<file name="..." lang="...">code</file>` children.
|
|
101
|
-
- **Optional on `<code-snippet>`**: `id`.
|
|
102
|
-
- **Required on `<file>`**: `name`.
|
|
103
|
-
- **Optional on `<file>`**: `lang`.
|
|
104
|
-
- **Body of `<file>`**: raw code text; use CDATA for multi-line or XML-sensitive code.
|
|
105
|
-
|
|
106
|
-
```xml
|
|
107
|
-
<code-snippet>
|
|
108
|
-
<file name="index.ts" lang="ts"><![CDATA[
|
|
109
|
-
export const main = () => console.log('hi');
|
|
110
|
-
]]></file>
|
|
111
|
-
<file name="package.json" lang="json"><![CDATA[
|
|
112
|
-
{ "type": "module" }
|
|
113
|
-
]]></file>
|
|
114
|
-
</code-snippet>
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Math
|
|
118
|
-
|
|
119
|
-
### `<math>` — inline or block equation
|
|
120
|
-
|
|
121
|
-
- **When**: A KaTeX equation.
|
|
122
|
-
- **Required**: equation text in the body.
|
|
123
|
-
- **Optional**: `display` (`"block"` for standalone equation; omit for inline), `color` (only honored on inline math).
|
|
124
|
-
- **Body**: equation source.
|
|
125
|
-
|
|
126
|
-
```xml
|
|
127
|
-
<p>Inline: <math>E = mc^2</math>.</p>
|
|
128
|
-
|
|
129
|
-
<math display="block">\int_0^1 x^2\, dx = \tfrac{1}{3}</math>
|
|
130
|
-
|
|
131
|
-
<p><math color="#ff5577">a^2 + b^2 = c^2</math></p>
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
The reader chooses `katex-block` vs `katex-inline` based on `display`. Block math is rendered in its own block; inline math sits inside paragraph text.
|
|
135
|
-
|
|
136
|
-
## Diagrams
|
|
137
|
-
|
|
138
|
-
### `<mermaid>` — Mermaid diagram
|
|
139
|
-
|
|
140
|
-
- **When**: A diagram expressible as Mermaid source.
|
|
141
|
-
- **Avoid when**: Freeform / hand-drawn diagram — use `<excalidraw>`.
|
|
142
|
-
- **Required**: none.
|
|
143
|
-
- **Optional**: `id`.
|
|
144
|
-
- **Body**: Mermaid source text. Use ` ` to encode newlines if you must keep the body on one line; otherwise normal element text with real newlines is fine.
|
|
145
|
-
|
|
146
|
-
```xml
|
|
147
|
-
<mermaid>
|
|
148
|
-
flowchart LR
|
|
149
|
-
A --> B
|
|
150
|
-
B --> C
|
|
151
|
-
</mermaid>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### `<excalidraw>` — opaque Excalidraw snapshot
|
|
155
|
-
|
|
156
|
-
- **When**: An opaque drawing snapshot that must round-trip exactly.
|
|
157
|
-
- **Avoid when**: A diagram you would otherwise express in Mermaid — pick `<mermaid>`.
|
|
158
|
-
- **Required**: snapshot body (preferred) or `snapshot` attribute (legacy).
|
|
159
|
-
- **Optional**: `id`, `snapshot` (legacy attribute form).
|
|
160
|
-
- **Body**: opaque JSON inside `<![CDATA[...]]>`. Empty snapshots may be self-closing.
|
|
161
|
-
|
|
162
|
-
```xml
|
|
163
|
-
<excalidraw><![CDATA[{"elements":[],"appState":{}}]]></excalidraw>
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
## Callouts and admonitions
|
|
167
|
-
|
|
168
|
-
### `<alert>` — semantic admonition
|
|
169
|
-
|
|
170
|
-
- **When**: Inline-of-section semantic admonitions (note, tip, warning, important, caution).
|
|
171
|
-
- **Avoid when**: Top-of-page announcement — use `<banner>`.
|
|
172
|
-
- **Required**: none (empty body allowed).
|
|
173
|
-
- **Optional**: `id`, `type` (`note` default | `tip` | `important` | `warning` | `caution`).
|
|
174
|
-
- **Body**: nested LiteXML block content (usually `<p>...</p>`). Stored as a nested `SerializedEditorState`.
|
|
175
|
-
|
|
176
|
-
```xml
|
|
177
|
-
<alert type="warning">
|
|
178
|
-
<p>This operation cannot be undone.</p>
|
|
179
|
-
</alert>
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### `<banner>` — prominent editorial notice
|
|
183
|
-
|
|
184
|
-
- **When**: Page-level announcement or contextual banner.
|
|
185
|
-
- **Avoid when**: Inline admonition — use `<alert>`.
|
|
186
|
-
- **Required**: none.
|
|
187
|
-
- **Optional**: `id`, `type`. Renderer-supported values include `note`, `tip`, `important`, `warning`, `caution`, plus informational `info` and `success`. Pick the value consistent with the target renderer.
|
|
188
|
-
- **Body**: nested LiteXML block content.
|
|
189
|
-
|
|
190
|
-
```xml
|
|
191
|
-
<banner type="info">
|
|
192
|
-
<p>The article was updated on 2026-05-16.</p>
|
|
193
|
-
</banner>
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Containers
|
|
197
|
-
|
|
198
|
-
### `<details>` — collapsible
|
|
199
|
-
|
|
200
|
-
- **When**: Optional secondary explanation that should default to collapsed.
|
|
201
|
-
- **Avoid when**: Required reading — render inline.
|
|
202
|
-
- **Required**: none.
|
|
203
|
-
- **Optional**: `id`, `summary` (visible collapsed label), `open` (`"true"` to start expanded; omit otherwise).
|
|
204
|
-
- **Body**: block children.
|
|
205
|
-
|
|
206
|
-
```xml
|
|
207
|
-
<details summary="Implementation notes" open="false">
|
|
208
|
-
<p>Internals…</p>
|
|
209
|
-
</details>
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### `<nested-doc>` — independently editable nested document
|
|
213
|
-
|
|
214
|
-
- **When**: A nested editor state that the consumer renders or edits as its own document.
|
|
215
|
-
- **Avoid when**: Plain grouping — use `<grid>` or just sequential blocks.
|
|
216
|
-
- **Required**: none.
|
|
217
|
-
- **Optional**: `id`.
|
|
218
|
-
- **Body**: nested LiteXML block content (becomes its own `SerializedEditorState`).
|
|
219
|
-
|
|
220
|
-
```xml
|
|
221
|
-
<nested-doc>
|
|
222
|
-
<h3>Nested section</h3>
|
|
223
|
-
<p>Body.</p>
|
|
224
|
-
</nested-doc>
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### `<grid>` — editorial multi-column layout
|
|
228
|
-
|
|
229
|
-
- **When**: Two-up / three-up editorial layout where each cell is independent.
|
|
230
|
-
- **Avoid when**: Data with row/column semantics — use `<table>`.
|
|
231
|
-
- **Required**: one or more `<cell>...</cell>` children.
|
|
232
|
-
- **Optional**: `id`, `cols` (numeric, default `2`), `gap` (CSS length string, default `16px`).
|
|
233
|
-
- **Body of `<cell>`**: nested LiteXML block content (becomes its own `SerializedEditorState`).
|
|
234
|
-
|
|
235
|
-
```xml
|
|
236
|
-
<grid cols="3" gap="24px">
|
|
237
|
-
<cell><h3>A</h3><p>One</p></cell>
|
|
238
|
-
<cell><h3>B</h3><p>Two</p></cell>
|
|
239
|
-
<cell><h3>C</h3><p>Three</p></cell>
|
|
240
|
-
</grid>
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Inline annotations
|
|
244
|
-
|
|
245
|
-
### `<spoiler>` — hidden inline text
|
|
246
|
-
|
|
247
|
-
- **When**: Text the reader can intentionally reveal.
|
|
248
|
-
- **Avoid when**: A collapsible block — use `<details>`.
|
|
249
|
-
- **Required**: none.
|
|
250
|
-
- **Optional**: none.
|
|
251
|
-
- **Body**: inline children. Must appear inside an inline-accepting parent (e.g. `<p>`).
|
|
252
|
-
|
|
253
|
-
```xml
|
|
254
|
-
<p>The killer is <spoiler>the butler</spoiler>.</p>
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### `<ruby>` — East Asian ruby annotation
|
|
258
|
-
|
|
259
|
-
- **When**: A base text needs a pronunciation/reading overlay.
|
|
260
|
-
- **Avoid when**: A plain parenthetical — use prose.
|
|
261
|
-
- **Required**: base text in the body.
|
|
262
|
-
- **Optional**: `rt` (the reading).
|
|
263
|
-
- **Body**: inline base text.
|
|
264
|
-
|
|
265
|
-
```xml
|
|
266
|
-
<p><ruby rt="haklex">Haklex</ruby> エディタ。</p>
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### `<mention>` — handle reference
|
|
270
|
-
|
|
271
|
-
- **When**: A person or account reference that carries platform + handle metadata.
|
|
272
|
-
- **Avoid when**: Plain name with no identity metadata — write prose.
|
|
273
|
-
- **Required**: `platform`, `handle`.
|
|
274
|
-
- **Optional**: none.
|
|
275
|
-
- **Body**: display name text. Defaults to handle if empty.
|
|
276
|
-
|
|
277
|
-
```xml
|
|
278
|
-
<p>Author: <mention platform="github" handle="innei">Innei</mention>.</p>
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
### `<tag>` — inline topical label
|
|
282
|
-
|
|
283
|
-
- **When**: An inline tag chip (`#ai`, `#editor`).
|
|
284
|
-
- **Avoid when**: A document-level section category — use a heading.
|
|
285
|
-
- **Required**: text body.
|
|
286
|
-
- **Optional**: none.
|
|
287
|
-
- **Body**: plain text.
|
|
288
|
-
|
|
289
|
-
```xml
|
|
290
|
-
<p>Topics: <tag>AI</tag> <tag>Lexical</tag>.</p>
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### `<comment>` — inline reviewer annotation
|
|
294
|
-
|
|
295
|
-
- **When**: An inline note that should render as a reviewer comment (yellow highlight, side margin marker, etc.).
|
|
296
|
-
- **Avoid when**: Reader-facing aside — use prose, `<alert>`, or `<details>`.
|
|
297
|
-
- **Required**: text body.
|
|
298
|
-
- **Optional**: none.
|
|
299
|
-
- **Body**: plain text.
|
|
300
|
-
|
|
301
|
-
```xml
|
|
302
|
-
<p>The pipeline scales linearly<comment>citation needed</comment>.</p>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### `<footnote>` and `<footnote-section>` — footnotes
|
|
306
|
-
|
|
307
|
-
- **When**: Academic-style references with collapsible definitions.
|
|
308
|
-
- **Avoid when**: Primary content — keep it in the body.
|
|
309
|
-
|
|
310
|
-
`<footnote>`:
|
|
311
|
-
|
|
312
|
-
- **Required**: `ref` — must match a `<def ref="...">` later.
|
|
313
|
-
- **Optional**: none.
|
|
314
|
-
- **Body**: self-closing.
|
|
315
|
-
|
|
316
|
-
`<footnote-section>`:
|
|
317
|
-
|
|
318
|
-
- **Required**: one or more `<def ref="...">definition text</def>`.
|
|
319
|
-
- **Optional**: `id` on `<footnote-section>`.
|
|
320
|
-
- **Body**: `<def>` children only.
|
|
321
|
-
|
|
322
|
-
```xml
|
|
323
|
-
<p>Lexical 0.44<footnote ref="1" /> is the runtime.</p>
|
|
324
|
-
…
|
|
325
|
-
<footnote-section>
|
|
326
|
-
<def ref="1">See Facebook's Lexical 0.44 release notes.</def>
|
|
327
|
-
</footnote-section>
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## Interactive
|
|
331
|
-
|
|
332
|
-
### `<chat>` — conversation transcript
|
|
333
|
-
|
|
334
|
-
- **When**: A user/agent or user/user transcript that should render as chat bubbles.
|
|
335
|
-
- **Avoid when**: A normal quote — use `<blockquote>`.
|
|
336
|
-
- **Required**: `<participants>` block + `<messages>` block.
|
|
337
|
-
- **Optional on `<chat>`**: `id`, `variant` (`user-agent` default | `user-user`).
|
|
338
|
-
- **Required on `<participant>`**: `kind` (`user` | `agent`).
|
|
339
|
-
- **Optional on `<participant>`**: `id` (auto-minted if missing), `name`, `avatar`.
|
|
340
|
-
- **Required on `<message>`**: `participant` (matching a participant `id`).
|
|
341
|
-
- **Optional on `<message>`**: `id` (auto-minted if missing).
|
|
342
|
-
- **Body of `<message>`**: plain text.
|
|
343
|
-
|
|
344
|
-
```xml
|
|
345
|
-
<chat variant="user-agent">
|
|
346
|
-
<participants>
|
|
347
|
-
<participant id="u1" kind="user" name="Innei" />
|
|
348
|
-
<participant id="a1" kind="agent" name="Haklex" />
|
|
349
|
-
</participants>
|
|
350
|
-
<messages>
|
|
351
|
-
<message id="m1" participant="u1">How do I add a node?</message>
|
|
352
|
-
<message id="m2" participant="a1">Register a reader and a writer in default-registry.ts.</message>
|
|
353
|
-
</messages>
|
|
354
|
-
</chat>
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### `<poll>` — reader choice / survey
|
|
358
|
-
|
|
359
|
-
- **When**: A live poll readers can vote on.
|
|
360
|
-
- **Avoid when**: A static list of options — use `<ul>` / `<ol>`.
|
|
361
|
-
- **Required**: one `<question>` + one or more `<option>`.
|
|
362
|
-
- **Optional on `<poll>`**: `id`, `poll-id` (auto-minted if missing), `mode` (`single` default | `multiple`), `close-at` (ISO-like timestamp), `show-results` (`always` | `after-vote` | `after-close`).
|
|
363
|
-
- **Optional on `<option>`**: `id` (auto-minted if missing).
|
|
364
|
-
- **Body of `<question>`** / **`<option>`**: plain text.
|
|
365
|
-
|
|
366
|
-
```xml
|
|
367
|
-
<poll mode="single" show-results="after-vote">
|
|
368
|
-
<question>Pick a runtime</question>
|
|
369
|
-
<option>Bun</option>
|
|
370
|
-
<option>Node</option>
|
|
371
|
-
<option>Deno</option>
|
|
372
|
-
</poll>
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
For freshly authored polls, **omit** `poll-id` and option `id` — the reader mints them. Preserve them when editing an existing poll so vote counts stay attached to the right option.
|
|
376
|
-
|
|
377
|
-
## Internal / agent
|
|
378
|
-
|
|
379
|
-
### `<agent-diff>` — review/diff marker
|
|
380
|
-
|
|
381
|
-
- **When**: Internal agent review UI rendering inline diff markers.
|
|
382
|
-
- **Avoid when**: Normal article authoring — do not introduce unless implementing the review pipeline.
|
|
383
|
-
- **Required**: none.
|
|
384
|
-
- **Optional**: `id`, `op` (defaults to `insert`), `entry` (id of a diff entry to link to).
|
|
385
|
-
- **Body**: self-closing.
|
|
386
|
-
|
|
387
|
-
```xml
|
|
388
|
-
<agent-diff op="insert" entry="diff-7" />
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
## Cross-cutting rules
|
|
392
|
-
|
|
393
|
-
- **`id` attribute** on any block-level extension sets `$.blockId`. Omit for fresh content; preserve when editing.
|
|
394
|
-
- **Quoting**: all attribute values must be quoted.
|
|
395
|
-
- **Escaping**: in element text, escape `&` `<` `>` `"` as `&` `<` `>` `"`.
|
|
396
|
-
- **CDATA**: required for opaque JSON snapshots (`<excalidraw>`), strongly recommended for multi-line or XML-sensitive code bodies (`<codeblock>`, `<code-snippet><file>`).
|
|
397
|
-
- **Nested block content** is required inside `<alert>`, `<banner>`, `<nested-doc>`, `<details>`, `<grid><cell>`. The body becomes a fresh `SerializedEditorState` rooted at the container, so Markdown does **not** work inside them.
|
|
398
|
-
- **Canonical tag names** only. Do not emit legacy aliases (`<linkcard>`, `<codesnippet>`, `<nesteddoc>`, `<code-block>`).
|
|
399
|
-
- **Unregistered tags** serialize to `<node type="..." data='{...}' />` — data is preserved but opaque. Always prefer a registered tag.
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Structural and inline-formatting nodes
|
|
2
|
-
|
|
3
|
-
Tags in this file are the standard CommonMark-shaped building blocks. They are always available without any extension package. Use them to build the prose around extension nodes. If a fragment contains **any** LiteXML extension tag, the surrounding prose must also be expressed via these tags — Markdown syntax is not parsed inside LiteXML fragments.
|
|
4
|
-
|
|
5
|
-
For the Haklex-specific tags (alerts, banners, code blocks, math, etc.), see [`nodes-extensions.md`](./nodes-extensions.md).
|
|
6
|
-
|
|
7
|
-
## Block tags
|
|
8
|
-
|
|
9
|
-
### `<p>` — paragraph
|
|
10
|
-
|
|
11
|
-
- **When**: Default container for ordinary prose. The body of nearly every block-level extension (`<alert>`, `<banner>`, `<details>`, `<nested-doc>`, `<grid><cell>`, `<blockquote>`, `<li>`) is one or more `<p>` elements.
|
|
12
|
-
- **Avoid when**: The content is a heading, list item, or any specialized container — use the matching tag instead.
|
|
13
|
-
- **Required**: none.
|
|
14
|
-
- **Optional**: `id` (sets `$.blockId` for stable references).
|
|
15
|
-
- **Body**: inline content (text, links, inline formatting tags, inline extension nodes).
|
|
16
|
-
|
|
17
|
-
```xml
|
|
18
|
-
<p>Plain prose with an <a href="https://example.com">inline link</a>.</p>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### `<h1>` … `<h6>` — headings
|
|
22
|
-
|
|
23
|
-
- **When**: Section hierarchy and document scan anchors.
|
|
24
|
-
- **Avoid when**: You only want to style text — headings imply structural meaning and contribute to the document outline.
|
|
25
|
-
- **Required**: none.
|
|
26
|
-
- **Optional**: `id`.
|
|
27
|
-
- **Body**: inline content.
|
|
28
|
-
|
|
29
|
-
```xml
|
|
30
|
-
<h2 id="install">Installation</h2>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### `<blockquote>` — quote
|
|
34
|
-
|
|
35
|
-
- **When**: Quoted external text or cited excerpt. The reader emits `type: "rich-quote"` when `attribution` is present, otherwise `type: "quote"`. Both hydrate to `RichQuoteNode` in the editor; the distinction is a writer-side optimization for fixtures without attribution.
|
|
36
|
-
- **Avoid when**: Warnings or editorial callouts — use `<alert>` or `<banner>`.
|
|
37
|
-
- **Required**: at least one block child (typically `<p>`).
|
|
38
|
-
- **Optional**: `id`, `attribution` (citation line — author, source, work).
|
|
39
|
-
- **Body**: block children.
|
|
40
|
-
|
|
41
|
-
```xml
|
|
42
|
-
<blockquote attribution="— Wang Xizhi, Lantingji Xu">
|
|
43
|
-
<p>未尝不临文嗟悼,不能喻之于怀。</p>
|
|
44
|
-
</blockquote>
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### `<ul>`, `<ol>` — lists
|
|
48
|
-
|
|
49
|
-
- **When**: Sequences of items. `<ul>` for unordered, `<ol>` for ordered. Set `type="check"` on `<ul>` for a task list.
|
|
50
|
-
- **Avoid when**: Two-dimensional comparison — use `<table>` or `<grid>`. Boolean survey choices — use `<poll>`.
|
|
51
|
-
- **Required**: one or more `<li>` children.
|
|
52
|
-
- **Optional on `<ul>`**: `id`, `type="check"`.
|
|
53
|
-
- **Optional on `<ol>`**: `id`, `start` (numeric starting value, default `1`).
|
|
54
|
-
|
|
55
|
-
```xml
|
|
56
|
-
<ul>
|
|
57
|
-
<li><p>First</p></li>
|
|
58
|
-
<li><p>Second</p></li>
|
|
59
|
-
</ul>
|
|
60
|
-
|
|
61
|
-
<ol start="3">
|
|
62
|
-
<li><p>Third</p></li>
|
|
63
|
-
</ol>
|
|
64
|
-
|
|
65
|
-
<ul type="check">
|
|
66
|
-
<li checked="true"><p>Done</p></li>
|
|
67
|
-
<li checked="false"><p>Todo</p></li>
|
|
68
|
-
</ul>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### `<li>` — list item
|
|
72
|
-
|
|
73
|
-
- **Required**: at least one block child.
|
|
74
|
-
- **Optional**: `id`, `checked` (`"true"` / `"false"`, only meaningful inside `<ul type="check">`).
|
|
75
|
-
- **Body**: block children (usually a single `<p>`; nested `<ul>` / `<ol>` is allowed).
|
|
76
|
-
|
|
77
|
-
### `<table>`, `<tr>`, `<th>`, `<td>` — tables
|
|
78
|
-
|
|
79
|
-
- **When**: Dense factual comparison with rows and columns.
|
|
80
|
-
- **Avoid when**: Editorial multi-column layout — use `<grid>`.
|
|
81
|
-
- **Required**: `<table>` contains `<tr>`; each `<tr>` contains `<th>` or `<td>`; each cell contains block children (typically `<p>`).
|
|
82
|
-
- **Optional**: `id` on `<table>`.
|
|
83
|
-
- **Body**: cells contain `<p>` (or other block content). Header state is encoded by the tag — `<th>` for headers, `<td>` for data cells.
|
|
84
|
-
|
|
85
|
-
```xml
|
|
86
|
-
<table>
|
|
87
|
-
<tr>
|
|
88
|
-
<th><p>Tag</p></th>
|
|
89
|
-
<th><p>Use</p></th>
|
|
90
|
-
</tr>
|
|
91
|
-
<tr>
|
|
92
|
-
<td><p>p</p></td>
|
|
93
|
-
<td><p>Paragraph</p></td>
|
|
94
|
-
</tr>
|
|
95
|
-
</table>
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### `<hr />` — horizontal rule
|
|
99
|
-
|
|
100
|
-
- **When**: Hard section break.
|
|
101
|
-
- **Required**: self-closing.
|
|
102
|
-
- **Optional**: `id`.
|
|
103
|
-
|
|
104
|
-
```xml
|
|
105
|
-
<hr />
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Inline tags
|
|
109
|
-
|
|
110
|
-
### `<a>` — link
|
|
111
|
-
|
|
112
|
-
- **When**: Inline citation or hyperlink.
|
|
113
|
-
- **Avoid when**: The URL deserves a rich preview — use `<link-card>` (see extensions).
|
|
114
|
-
- **Required**: `href` attribute.
|
|
115
|
-
- **Optional**: `target`, `title`.
|
|
116
|
-
- **Body**: inline children (text and inline formatting tags).
|
|
117
|
-
|
|
118
|
-
```xml
|
|
119
|
-
<a href="https://example.com" target="_blank" title="More info">Example</a>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`<a>` writers emit either `type: "link"` or `type: "autolink"` depending on origin; both hydrate to a `LinkNode`. From an authoring perspective, always emit `<a>`.
|
|
123
|
-
|
|
124
|
-
### `<br />` — line break
|
|
125
|
-
|
|
126
|
-
Soft break inside a paragraph. Self-closing.
|
|
127
|
-
|
|
128
|
-
### Inline formatting tags
|
|
129
|
-
|
|
130
|
-
These map to text-node format flags. Wrap inline text only.
|
|
131
|
-
|
|
132
|
-
| Format | LiteXML |
|
|
133
|
-
| ----------- | ------------------------------------------------------------ |
|
|
134
|
-
| Bold | `<b>text</b>` or `<strong>text</strong>` |
|
|
135
|
-
| Italic | `<i>text</i>` or `<em>text</em>` |
|
|
136
|
-
| Strike | `<s>text</s>`, `<del>text</del>`, or `<strike>text</strike>` |
|
|
137
|
-
| Underline | `<u>text</u>` |
|
|
138
|
-
| Inline code | `<code>text</code>` |
|
|
139
|
-
| Subscript | `<sub>text</sub>` |
|
|
140
|
-
| Superscript | `<sup>text</sup>` |
|
|
141
|
-
| Highlight | `<mark>text</mark>` |
|
|
142
|
-
|
|
143
|
-
These tags **must** appear inside an inline-accepting parent (`<p>`, `<h1>`-`<h6>`, `<li>`, `<th>`, `<td>`, etc.). Nesting is allowed: `<b><i>both</i></b>`.
|
|
144
|
-
|
|
145
|
-
## Cross-cutting rules
|
|
146
|
-
|
|
147
|
-
- **`id` attribute** on any block tag sets `$.blockId`, which is the Lexical-side anchor used by tool calls that need to target a specific block. Omit for fresh content; preserve when editing existing fragments.
|
|
148
|
-
- **Attribute quoting**: all attribute values must be quoted (`<p id="intro">`, not `<p id=intro>`).
|
|
149
|
-
- **XML escaping** in body text: `&`, `<`, `>`, `"`. Use CDATA when the body is opaque or contains many sensitive characters (typically only needed for code / drawing snapshots, not for ordinary prose).
|
|
150
|
-
- **Canonical tag names**: lowercase, hyphenated. Do not emit legacy aliases (`<linkcard>`, `<codesnippet>`, `<nesteddoc>`, `<code-block>`).
|