mdbrowse-cli 0.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/README.md +64 -0
- package/bin/mdnow.js +80 -0
- package/package.json +46 -0
- package/public/app.js +658 -0
- package/public/index.html +46 -0
- package/public/style.css +879 -0
- package/src/renderer.js +170 -0
- package/src/scanner.js +96 -0
- package/src/search.js +118 -0
- package/src/server.js +299 -0
- package/src/tunnel.js +61 -0
- package/src/watcher.js +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# mdbrowse
|
|
2
|
+
|
|
3
|
+
**Browse and preview markdown files in any directory.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/mdbrowse)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Zero-install CLI that spins up a local web UI with a file tree, rendered markdown, live reload, and optional Cloudflare Tunnel for remote access.
|
|
9
|
+
|
|
10
|
+
<!-- TODO: Add screenshot here -->
|
|
11
|
+
<!--  -->
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx mdbrowse . # serve current directory
|
|
17
|
+
npx mdbrowse ./docs # serve a specific folder
|
|
18
|
+
npx mdbrowse . --tunnel # expose via Cloudflare Tunnel
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Opens in your browser automatically. On SSH/headless servers, grab the printed URL.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- 📁 **File tree sidebar** — browse all files in the directory
|
|
26
|
+
- 📝 **Markdown rendering** — GFM tables, task lists, strikethrough, and more
|
|
27
|
+
- 🎨 **Syntax highlighting** — VS Code-quality code blocks via Shiki
|
|
28
|
+
- 🔢 **Math & diagrams** — LaTeX math (KaTeX) and Mermaid diagrams
|
|
29
|
+
- 📋 **Frontmatter** — YAML frontmatter displayed as a clean table
|
|
30
|
+
- 🔴 **Live reload** — auto-refreshes when files change on disk
|
|
31
|
+
- ✏️ **Edit mode** — toggle to edit, Ctrl+S to save, tab indentation
|
|
32
|
+
- 🔍 **Search** — filename + content search, Ctrl+K to open
|
|
33
|
+
- 🌗 **Dark / light theme** — auto-detects system preference, with toggle
|
|
34
|
+
- 🌐 **Cloudflare Tunnel** — instant public URL with `--tunnel`
|
|
35
|
+
- 🔒 **Basic auth** — protect access with `--auth user:pass`
|
|
36
|
+
- 🚫 **Read-only mode** — disable editing with `--read-only`
|
|
37
|
+
|
|
38
|
+
## CLI flags
|
|
39
|
+
|
|
40
|
+
| Flag | Default | Description |
|
|
41
|
+
|------|---------|-------------|
|
|
42
|
+
| `[directory]` | `.` | Directory to serve |
|
|
43
|
+
| `-p, --port <number>` | `3000` | Port to listen on |
|
|
44
|
+
| `--host <address>` | `localhost` | Host to bind to (use `0.0.0.0` for all interfaces) |
|
|
45
|
+
| `--tunnel` | off | Expose via Cloudflare Tunnel (requires `cloudflared`) |
|
|
46
|
+
| `--auth <user:pass>` | off | Require basic HTTP authentication |
|
|
47
|
+
| `--read-only` | off | Disable file editing |
|
|
48
|
+
| `--no-ignore` | off | Show all files (don't respect `.gitignore`) |
|
|
49
|
+
|
|
50
|
+
## Use cases
|
|
51
|
+
|
|
52
|
+
**Remote / headless servers** — Working on a cloud dev box or VPS? Run `npx mdbrowse . --tunnel` to get a public URL and view rendered markdown from any browser.
|
|
53
|
+
|
|
54
|
+
**AI coding tools** — Using Claude Code, Codex, or similar tools that generate lots of markdown? Browse their output rendered, not raw.
|
|
55
|
+
|
|
56
|
+
**Documentation browsing** — Point it at your `docs/` folder for a quick local docs site with search, live reload, and edit support.
|
|
57
|
+
|
|
58
|
+
## Tech stack
|
|
59
|
+
|
|
60
|
+
[Hono](https://hono.dev/) server, [unified](https://unifiedjs.com/)/remark markdown pipeline, [Shiki](https://shiki.style/) syntax highlighting, [KaTeX](https://katex.org/) math, [Mermaid](https://mermaid.js.org/) diagrams, [chokidar](https://github.com/paulmillr/chokidar) file watching, vanilla JS frontend — no build step.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
[MIT](LICENSE)
|
package/bin/mdnow.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { startServer } from '../src/server.js';
|
|
5
|
+
|
|
6
|
+
const program = new Command();
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.name('mdnow')
|
|
10
|
+
.description('Browse and preview markdown files in any directory')
|
|
11
|
+
.version('0.1.0')
|
|
12
|
+
.argument('[directory]', 'Directory to serve', '.')
|
|
13
|
+
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
14
|
+
.option('--host <address>', 'Host to bind to', 'localhost')
|
|
15
|
+
.option('--no-ignore', 'Show all files (ignore .gitignore)')
|
|
16
|
+
.option('--auth <credentials>', 'Require basic auth (user:pass)')
|
|
17
|
+
.option('--read-only', 'Disable file editing')
|
|
18
|
+
.option('--tunnel', 'Expose via Cloudflare Tunnel (requires cloudflared)')
|
|
19
|
+
.action(async (directory, options) => {
|
|
20
|
+
const port = parseInt(options.port, 10);
|
|
21
|
+
const host = options.host;
|
|
22
|
+
const respectIgnore = options.ignore !== false;
|
|
23
|
+
const readOnly = !!options.readOnly;
|
|
24
|
+
|
|
25
|
+
let auth;
|
|
26
|
+
if (options.auth) {
|
|
27
|
+
if (!options.auth.includes(':')) {
|
|
28
|
+
console.error('Error: --auth must be in user:pass format');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const [user, ...rest] = options.auth.split(':');
|
|
32
|
+
auth = { username: user, password: rest.join(':') };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
await startServer({
|
|
36
|
+
directory,
|
|
37
|
+
port,
|
|
38
|
+
host,
|
|
39
|
+
respectIgnore,
|
|
40
|
+
auth,
|
|
41
|
+
readOnly,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const url = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
|
|
45
|
+
console.log(`\n mdnow serving ${directory}`);
|
|
46
|
+
console.log(` → ${url}\n`);
|
|
47
|
+
|
|
48
|
+
if (options.tunnel) {
|
|
49
|
+
const { startTunnel, registerCleanup } = await import('../src/tunnel.js');
|
|
50
|
+
try {
|
|
51
|
+
console.log(' Starting Cloudflare Tunnel...');
|
|
52
|
+
const { url: tunnelUrl, child } = await startTunnel(port);
|
|
53
|
+
registerCleanup(child);
|
|
54
|
+
console.log(` → ${tunnelUrl}\n`);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error(`\n ${err.message}\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Open browser if not in a headless/SSH environment
|
|
62
|
+
const isHeadless = !!(
|
|
63
|
+
process.env.SSH_CLIENT ||
|
|
64
|
+
process.env.SSH_TTY ||
|
|
65
|
+
process.env.SSH_CONNECTION
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!isHeadless) {
|
|
69
|
+
const { exec } = await import('child_process');
|
|
70
|
+
const cmd =
|
|
71
|
+
process.platform === 'darwin'
|
|
72
|
+
? 'open'
|
|
73
|
+
: process.platform === 'win32'
|
|
74
|
+
? 'start'
|
|
75
|
+
: 'xdg-open';
|
|
76
|
+
exec(`${cmd} ${url}`);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mdbrowse-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to browse and preview markdown files in any directory",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mdnow": "bin/mdnow.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"src/",
|
|
12
|
+
"public/"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"markdown",
|
|
16
|
+
"browser",
|
|
17
|
+
"preview",
|
|
18
|
+
"cli",
|
|
19
|
+
"file-browser",
|
|
20
|
+
"live-reload"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "playwright test"
|
|
24
|
+
},
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@hono/node-server": "^1.14.0",
|
|
28
|
+
"chokidar": "^4.0.0",
|
|
29
|
+
"commander": "^13.1.0",
|
|
30
|
+
"gray-matter": "^4.0.0",
|
|
31
|
+
"hono": "^4.7.0",
|
|
32
|
+
"ignore": "^7.0.0",
|
|
33
|
+
"rehype-katex": "^7.0.0",
|
|
34
|
+
"rehype-stringify": "^10.0.0",
|
|
35
|
+
"remark-gfm": "^4.0.0",
|
|
36
|
+
"remark-html": "^16.0.0",
|
|
37
|
+
"remark-math": "^6.0.0",
|
|
38
|
+
"remark-parse": "^11.0.0",
|
|
39
|
+
"shiki": "^3.0.0",
|
|
40
|
+
"unified": "^11.0.0",
|
|
41
|
+
"ws": "^8.18.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@playwright/test": "^1.58.2"
|
|
45
|
+
}
|
|
46
|
+
}
|