memd-cli 3.0.2 → 3.1.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.
@@ -14,7 +14,10 @@
14
14
  "Bash(MEMD_THEME= pnpm test 2>&1 | tail -20)",
15
15
  "Bash(MEMD_THEME= npx vitest run 2>&1)",
16
16
  "Bash(node:*)",
17
- "Bash(printf:*)"
17
+ "Bash(printf:*)",
18
+ "WebFetch(domain:registry.npmjs.org)",
19
+ "WebFetch(domain:api.github.com)",
20
+ "Bash(mkdir:*)"
18
21
  ]
19
22
  }
20
23
  }
package/CLAUDE.md CHANGED
@@ -1,4 +1,73 @@
1
- ## how to test
1
+ # memd
2
2
 
3
- - `node main.js test/test1.md`
4
- - `node main.js test/test2.md`
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.2",
3
+ "version": "3.1.1",
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.3",
13
+ "@ktrysmt/beautiful-mermaid": "1.1.5",
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 = [
@@ -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/dotted.md ADDED
@@ -0,0 +1,16 @@
1
+
2
+ ```mermaid
3
+ graph TD
4
+ A[Push to master] --> B[GitHub Actions]
5
+ B --> C[Build Docker image]
6
+ C --> D[Push to GHCR]
7
+ E[devcontainer.json<br>references GHCR image] --> F[devcontainer up<br>on any repo]
8
+ D -.-> |docker pull| F
9
+ F --> G[postStartCommand]
10
+ subgraph container [Inside devcontainer]
11
+ G --> H[setup-claude.sh<br>symlink dotfiles +<br>merge settings.json]
12
+ G --> I[init-firewall.sh<br>iptables + ipset]
13
+ H --> J[Claude Code<br>--dangerously-skip-permissions]
14
+ I --> J
15
+ end
16
+ ```
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.2')
29
+ expect(output).toContain('3.1.1')
30
30
  })
31
31
 
32
32
  it.concurrent('--help', async () => {
@@ -122,6 +122,43 @@ 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
+
148
+ it.concurrent('renders dotted.md (dotted arrow with spaced label)', async () => {
149
+ const output = await run(
150
+ ['--no-pager', '--no-color', '--width', '100', 'test/dotted.md'],
151
+ )
152
+ // Dotted arrow line character (unicode mode)
153
+ expect(output).toContain('\u2506')
154
+ // Edge label
155
+ expect(output).toContain('docker pull')
156
+ // Key nodes
157
+ expect(output).toContain('Push to GHCR')
158
+ expect(output).toContain('devcontainer up')
159
+ expect(output).toContain('Inside devcontainer')
160
+ })
161
+
125
162
  it('reads markdown from stdin via shell', () => {
126
163
  const output = execSync(
127
164
  `echo '# stdin test' | node ${MAIN} --no-pager --no-color`,