mind-hiro 0.2.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 +102 -0
- package/dist-lib/shared/types.d.ts +4 -0
- package/dist-lib/shared/types.js +1 -0
- package/dist-lib/src/cli.d.ts +2 -0
- package/dist-lib/src/cli.js +107 -0
- package/dist-lib/src/generator.d.ts +2 -0
- package/dist-lib/src/generator.js +17 -0
- package/dist-lib/src/index.d.ts +2 -0
- package/dist-lib/src/index.js +1 -0
- package/package.json +53 -0
- package/template/index.html +483 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Mind Hiro
|
|
2
|
+
|
|
3
|
+
Generate a **self-contained, interactive mind-map website** from a folder of Markdown files — one HTML file, works offline, no server needed.
|
|
4
|
+
|
|
5
|
+
Powered by [markmap](https://markmap.js.org/) + [Vite](https://vitejs.dev/).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Three-panel UI** — sidebar file list, Markdown editor, live mind-map renderer
|
|
12
|
+
- **Live editing** — edit Markdown in the browser; mind map updates in real time
|
|
13
|
+
- **Search** — filter files by filename or content
|
|
14
|
+
- **Share links** — compress current Markdown into a URL hash and copy to clipboard
|
|
15
|
+
- **Dark / light mode** — persisted to `localStorage`
|
|
16
|
+
- **Local edits saved** — browser remembers your edits per file across sessions
|
|
17
|
+
- **Fully self-contained** — all JS/CSS inlined; no CDN, works offline
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## CLI
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx mind-hiro generate <dir> [options]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Options:
|
|
29
|
+
-o, --output <file> Output HTML file (default: mind-hiro.html)
|
|
30
|
+
-r, --recursive Scan subdirectories recursively
|
|
31
|
+
-h, --help Show help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Examples
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Generate from a docs folder
|
|
38
|
+
npx mind-hiro generate ./docs -o site.html
|
|
39
|
+
|
|
40
|
+
# Include subdirectories
|
|
41
|
+
npx mind-hiro generate ./notes -r -o notes.html
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then open `site.html` in any browser — no server needed.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Install globally
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm install -g mind-hiro
|
|
52
|
+
mind-hiro generate ./docs -o site.html
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Programmatic API
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { generate } from 'mind-hiro'
|
|
61
|
+
|
|
62
|
+
const html = generate([
|
|
63
|
+
{ name: 'Architecture', content: '# Architecture\n## Frontend\n## Backend' },
|
|
64
|
+
{ name: 'Roadmap', content: '# Roadmap\n## Q1\n## Q2' },
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
import { writeFileSync } from 'fs'
|
|
68
|
+
writeFileSync('output.html', html)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `generate(files): string`
|
|
72
|
+
|
|
73
|
+
| Parameter | Type | Description |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `files` | `MindMapFile[]` | Array of `{ name: string, content: string }` objects |
|
|
76
|
+
| returns | `string` | Self-contained HTML string |
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
interface MindMapFile {
|
|
80
|
+
name: string // Shown in the sidebar (filename without extension)
|
|
81
|
+
content: string // Raw Markdown content
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Development
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/your-username/mind-hiro
|
|
91
|
+
cd mind-hiro
|
|
92
|
+
npm install
|
|
93
|
+
|
|
94
|
+
npm run dev # Start Vite dev server
|
|
95
|
+
npm run build # Build app + CLI
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readdirSync, readFileSync, writeFileSync, statSync } from 'fs';
|
|
3
|
+
import { join, extname, basename, resolve } from 'path';
|
|
4
|
+
import { generate } from './generator.js';
|
|
5
|
+
function printHelp() {
|
|
6
|
+
console.log(`
|
|
7
|
+
mind-hiro — Generate an interactive mind map HTML site from Markdown files
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
mind-hiro generate <dir> [options]
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
-o, --output <file> Output HTML file path (default: mind-hiro.html)
|
|
14
|
+
-r, --recursive Scan subdirectories recursively
|
|
15
|
+
-h, --help Show this help message
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
mind-hiro generate ./docs -o site.html
|
|
19
|
+
mind-hiro generate ./notes -r -o notes.html
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
function collectFiles(dir, recursive) {
|
|
23
|
+
const files = [];
|
|
24
|
+
function scan(currentDir) {
|
|
25
|
+
let entries;
|
|
26
|
+
try {
|
|
27
|
+
entries = readdirSync(currentDir);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
console.error(`Error reading directory: ${currentDir}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
// Sort entries for consistent ordering
|
|
34
|
+
entries.sort();
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
const fullPath = join(currentDir, entry);
|
|
37
|
+
const stat = statSync(fullPath);
|
|
38
|
+
if (stat.isDirectory()) {
|
|
39
|
+
if (recursive)
|
|
40
|
+
scan(fullPath);
|
|
41
|
+
}
|
|
42
|
+
else if (extname(entry).toLowerCase() === '.md') {
|
|
43
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
44
|
+
const name = basename(entry, '.md');
|
|
45
|
+
files.push({ name, content });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
scan(resolve(dir));
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
function run() {
|
|
53
|
+
const args = process.argv.slice(2);
|
|
54
|
+
if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
|
|
55
|
+
printHelp();
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
const subcommand = args[0];
|
|
59
|
+
if (subcommand !== 'generate') {
|
|
60
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
61
|
+
printHelp();
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const rest = args.slice(1);
|
|
65
|
+
if (rest.length === 0 || rest[0].startsWith('-')) {
|
|
66
|
+
console.error('Error: <dir> argument is required');
|
|
67
|
+
printHelp();
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
const dir = rest[0];
|
|
71
|
+
let output = 'mind-hiro.html';
|
|
72
|
+
let recursive = false;
|
|
73
|
+
for (let i = 1; i < rest.length; i++) {
|
|
74
|
+
const flag = rest[i];
|
|
75
|
+
if (flag === '-o' || flag === '--output') {
|
|
76
|
+
const next = rest[++i];
|
|
77
|
+
if (!next) {
|
|
78
|
+
console.error('Error: --output requires a file path argument');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
output = next;
|
|
82
|
+
}
|
|
83
|
+
else if (flag === '-r' || flag === '--recursive') {
|
|
84
|
+
recursive = true;
|
|
85
|
+
}
|
|
86
|
+
else if (flag === '-h' || flag === '--help') {
|
|
87
|
+
printHelp();
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.error(`Unknown option: ${flag}`);
|
|
92
|
+
printHelp();
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
console.log(`Scanning ${dir}${recursive ? ' (recursive)' : ''}...`);
|
|
97
|
+
const files = collectFiles(dir, recursive);
|
|
98
|
+
if (files.length === 0) {
|
|
99
|
+
console.warn('Warning: No .md files found in the specified directory');
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
console.log(`Found ${files.length} file(s): ${files.map((f) => f.name).join(', ')}`);
|
|
103
|
+
const html = generate(files);
|
|
104
|
+
writeFileSync(output, html, 'utf-8');
|
|
105
|
+
console.log(`Generated ${output} (${Math.round(html.length / 1024)} KB)`);
|
|
106
|
+
}
|
|
107
|
+
run();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const TEMPLATE_PATH = join(__dirname, '../../template/index.html');
|
|
6
|
+
let _template = null;
|
|
7
|
+
function getTemplate() {
|
|
8
|
+
if (_template === null) {
|
|
9
|
+
_template = readFileSync(TEMPLATE_PATH, 'utf-8');
|
|
10
|
+
}
|
|
11
|
+
return _template;
|
|
12
|
+
}
|
|
13
|
+
export function generate(files) {
|
|
14
|
+
const template = getTemplate();
|
|
15
|
+
const json = JSON.stringify(files);
|
|
16
|
+
return template.replace('>[]</script>', `>${json}</script>`);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { generate } from './generator.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mind-hiro",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Generate a self-contained interactive mind-map website from Markdown files",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist-lib/src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mind-hiro": "./dist-lib/src/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist-lib/src/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist-lib",
|
|
17
|
+
"template"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "vite",
|
|
24
|
+
"build:app": "vite build",
|
|
25
|
+
"build:lib": "tsc -p tsconfig.node.json",
|
|
26
|
+
"build": "npm run build:app && npm run build:lib",
|
|
27
|
+
"prepublishOnly": "npm run build",
|
|
28
|
+
"preview": "vite preview",
|
|
29
|
+
"type-check": "tsc --noEmit && tsc -p tsconfig.node.json --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mindmap",
|
|
33
|
+
"markdown",
|
|
34
|
+
"markmap",
|
|
35
|
+
"visualization",
|
|
36
|
+
"cli",
|
|
37
|
+
"static-site"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^20.12.0",
|
|
43
|
+
"autoprefixer": "^10.4.19",
|
|
44
|
+
"lz-string": "^1.5.0",
|
|
45
|
+
"markmap-lib": "^0.18.12",
|
|
46
|
+
"markmap-view": "^0.18.12",
|
|
47
|
+
"postcss": "^8.4.38",
|
|
48
|
+
"tailwindcss": "^3.4.3",
|
|
49
|
+
"typescript": "^5.4.0",
|
|
50
|
+
"vite": "^5.2.0",
|
|
51
|
+
"vite-plugin-singlefile": "^2.0.0"
|
|
52
|
+
}
|
|
53
|
+
}
|