memd-cli 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/CLAUDE.md +72 -3
- package/main.js +1 -1
- package/package.json +2 -2
- package/render-shared.js +1 -1
- package/test/complex.md +21 -0
- package/test/memd.test.js +24 -1
package/CLAUDE.md
CHANGED
|
@@ -1,4 +1,73 @@
|
|
|
1
|
-
|
|
1
|
+
# memd
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Markdown viewer CLI with Mermaid diagram support. Terminal rendering and HTTP serve mode.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
memd/
|
|
9
|
+
main.js # CLI entry point (commander). Terminal render + `serve` sub-command
|
|
10
|
+
render-shared.js # HTML rendering: Mermaid SVG conversion, marked HTML output
|
|
11
|
+
render-utils.js # Pure helpers: escapeHtml, mixHex, resolveThemeColors
|
|
12
|
+
render-worker.js # Worker thread for serve mode (calls renderToHTML)
|
|
13
|
+
package.json
|
|
14
|
+
pnpm-lock.yaml
|
|
15
|
+
.npmrc
|
|
16
|
+
test/
|
|
17
|
+
memd.test.js # vitest tests (CLI, HTML output, serve, TTY, theme)
|
|
18
|
+
test1.md # basic markdown + mermaid
|
|
19
|
+
test2.md # complex mermaid diagram
|
|
20
|
+
test3.md # multiple mermaid blocks (marker ID uniqueness)
|
|
21
|
+
complex.md # graph TD with <br>, special chars, edge labels
|
|
22
|
+
test-br.md # <br> tag line breaks in mermaid nodes
|
|
23
|
+
test-cjk.md # Japanese labels in mermaid
|
|
24
|
+
test-highlight.md # syntax highlighting test
|
|
25
|
+
poc_md.ts # PoC script
|
|
26
|
+
poc_mermaid.ts # PoC script
|
|
27
|
+
pixel.png # test image for static file serving
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pnpm install # install dependencies
|
|
34
|
+
pnpm test # run tests (vitest run --maxConcurrency=20)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Manual testing
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
node main.js test/test1.md
|
|
41
|
+
node main.js test/test2.md
|
|
42
|
+
node main.js test/complex.md
|
|
43
|
+
node main.js --html test/test1.md # HTML output to stdout
|
|
44
|
+
node main.js serve --dir test --port 3000 # HTTP serve mode
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Key CLI flags
|
|
48
|
+
|
|
49
|
+
- `--no-pager` -- disable pager (less)
|
|
50
|
+
- `--no-color` -- strip ANSI escape codes
|
|
51
|
+
- `--ascii` -- ASCII-only diagram rendering
|
|
52
|
+
- `--html` -- output HTML instead of terminal
|
|
53
|
+
- `--theme <name>` -- color theme (default: `nord`)
|
|
54
|
+
- `--width <n>` -- terminal width override
|
|
55
|
+
|
|
56
|
+
## Environment variables
|
|
57
|
+
|
|
58
|
+
- `MEMD_THEME` -- default theme (overridden by `--theme` flag)
|
|
59
|
+
- `FORCE_COLOR=3` -- force truecolor ANSI output
|
|
60
|
+
|
|
61
|
+
## Available themes
|
|
62
|
+
|
|
63
|
+
nord, dracula, one-dark, github-dark, github-light, solarized-dark, solarized-light,
|
|
64
|
+
catppuccin-mocha, catppuccin-latte, tokyo-night, tokyo-night-storm, tokyo-night-light,
|
|
65
|
+
nord-light, zinc-dark, zinc-light
|
|
66
|
+
|
|
67
|
+
## Architecture notes
|
|
68
|
+
|
|
69
|
+
- `main.js` handles both terminal rendering (marked + marked-terminal + shiki) and `serve` sub-command (HTTP server with worker pool)
|
|
70
|
+
- `render-shared.js` converts Mermaid fenced blocks to SVG via `@ktrysmt/beautiful-mermaid`, then renders full HTML with `marked`
|
|
71
|
+
- `render-worker.js` runs `renderToHTML` in a worker thread for non-blocking serve mode
|
|
72
|
+
- `render-utils.js` provides theme color resolution and HTML escaping (shared by main.js and render-shared.js)
|
|
73
|
+
- Serve mode supports: directory listing, ETag/304 caching, gzip, static file serving (images/css), sidebar navigation, `--watch` with SSE live reload, CSP nonce
|
package/main.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @ts-nocheck
|
|
3
3
|
import { marked } from 'marked';
|
|
4
4
|
import { markedTerminal } from 'marked-terminal';
|
|
5
|
-
import { renderMermaidASCII, THEMES as MERMAID_THEMES } from 'beautiful-mermaid';
|
|
5
|
+
import { renderMermaidASCII, THEMES as MERMAID_THEMES } from '@ktrysmt/beautiful-mermaid';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { program } from 'commander';
|
|
8
8
|
import { spawn } from 'child_process';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memd-cli",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"test": "vitest run --maxConcurrency=20"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"beautiful-mermaid": "^1.1.
|
|
13
|
+
"@ktrysmt/beautiful-mermaid": "^1.1.4",
|
|
14
14
|
"chalk": "^5.6.2",
|
|
15
15
|
"commander": "^14.0.3",
|
|
16
16
|
"marked": "^17.0.4",
|
package/render-shared.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { Marked } from 'marked';
|
|
3
|
-
import { renderMermaidSVG } from 'beautiful-mermaid';
|
|
3
|
+
import { renderMermaidSVG } from '@ktrysmt/beautiful-mermaid';
|
|
4
4
|
import { escapeHtml, resolveThemeColors } from './render-utils.js';
|
|
5
5
|
|
|
6
6
|
export const MERMAID_MODAL_SCRIPT = [
|
package/test/complex.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
```mermaid
|
|
3
|
+
graph TD
|
|
4
|
+
A["AAA<br>(keita)"] --> C["CCC"]
|
|
5
|
+
B["BBB<br>(yuriko)"] --> C
|
|
6
|
+
C --> D["DDDD"]
|
|
7
|
+
D --> E["EEEE"]
|
|
8
|
+
|
|
9
|
+
A1["1 / 2"] --> A
|
|
10
|
+
A2["3 / 4"] --> A
|
|
11
|
+
A3["5 / 6"] --> A
|
|
12
|
+
A4["XXX<br>(YYY ZZZ)"] --> A
|
|
13
|
+
|
|
14
|
+
B1["77 77<br>(7 / 7 / 7)"] --> B
|
|
15
|
+
B2["88-88<br>(99 99)"] --> B
|
|
16
|
+
B3["111s 222s"] --> B
|
|
17
|
+
|
|
18
|
+
D --> F{"F?"}
|
|
19
|
+
F -->|Yes| G["High level<br>Tr"]
|
|
20
|
+
F -->|No| H["Dumb Tr<br>S"]
|
|
21
|
+
```
|
package/test/memd.test.js
CHANGED
|
@@ -26,7 +26,7 @@ function runSync(args) {
|
|
|
26
26
|
describe('memd CLI', () => {
|
|
27
27
|
it.concurrent('--version', async () => {
|
|
28
28
|
const output = await run(['-v'])
|
|
29
|
-
expect(output).toContain('3.0
|
|
29
|
+
expect(output).toContain('3.1.0')
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
it.concurrent('--help', async () => {
|
|
@@ -122,6 +122,29 @@ describe('memd CLI', () => {
|
|
|
122
122
|
expect(output).toContain('いいえ')
|
|
123
123
|
})
|
|
124
124
|
|
|
125
|
+
it.concurrent('renders complex.md (graph TD with <br> and special chars)', async () => {
|
|
126
|
+
const output = await run(
|
|
127
|
+
['--no-pager', '--no-color', '--width', '80', 'test/complex.md'],
|
|
128
|
+
)
|
|
129
|
+
// Node labels with <br> should render as multi-line text
|
|
130
|
+
expect(output).toContain('AAA')
|
|
131
|
+
expect(output).toContain('keita')
|
|
132
|
+
expect(output).toContain('BBB')
|
|
133
|
+
expect(output).toContain('yuriko')
|
|
134
|
+
// Leaf nodes
|
|
135
|
+
expect(output).toContain('1 / 2')
|
|
136
|
+
expect(output).toContain('XXX')
|
|
137
|
+
expect(output).toContain('YYY ZZZ')
|
|
138
|
+
// Decision node and edge labels
|
|
139
|
+
expect(output).toContain('F?')
|
|
140
|
+
expect(output).toContain('Yes')
|
|
141
|
+
expect(output).toContain('No')
|
|
142
|
+
expect(output).toContain('High level')
|
|
143
|
+
expect(output).toContain('Dumb Tr')
|
|
144
|
+
// <br> tags should not appear in rendered output
|
|
145
|
+
expect(output).not.toMatch(/<br\s*\/?>/)
|
|
146
|
+
})
|
|
147
|
+
|
|
125
148
|
it('reads markdown from stdin via shell', () => {
|
|
126
149
|
const output = execSync(
|
|
127
150
|
`echo '# stdin test' | node ${MAIN} --no-pager --no-color`,
|