wiki-viewer 1.0.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.
Files changed (119) hide show
  1. package/.next/standalone/.github/workflows/publish.yml +37 -0
  2. package/.next/standalone/DESIGN.md +521 -0
  3. package/.next/standalone/README.md +283 -0
  4. package/.next/standalone/bin/wiki-viewer.js +158 -0
  5. package/.next/standalone/next.config.ts +11 -0
  6. package/.next/standalone/package.json +120 -0
  7. package/.next/standalone/pnpm-lock.yaml +5654 -0
  8. package/.next/standalone/pnpm-workspace.yaml +2 -0
  9. package/.next/standalone/postcss.config.mjs +2 -0
  10. package/.next/standalone/public/logo.svg +11 -0
  11. package/.next/standalone/public/public/logo.svg +11 -0
  12. package/.next/standalone/server.js +44 -0
  13. package/.next/standalone/src/app/api/app-proxy/[...path]/route.ts +247 -0
  14. package/.next/standalone/src/app/api/assets/[...path]/route.ts +70 -0
  15. package/.next/standalone/src/app/api/system/browse/route.ts +78 -0
  16. package/.next/standalone/src/app/api/system/clear-root/route.ts +9 -0
  17. package/.next/standalone/src/app/api/system/config/route.ts +10 -0
  18. package/.next/standalone/src/app/api/system/pins/route.ts +31 -0
  19. package/.next/standalone/src/app/api/system/reveal/route.ts +28 -0
  20. package/.next/standalone/src/app/api/system/root-status/route.ts +9 -0
  21. package/.next/standalone/src/app/api/system/set-root/route.ts +23 -0
  22. package/.next/standalone/src/app/api/upload/[...path]/route.ts +80 -0
  23. package/.next/standalone/src/app/api/wiki/app/route.ts +41 -0
  24. package/.next/standalone/src/app/api/wiki/content/route.ts +60 -0
  25. package/.next/standalone/src/app/api/wiki/file/route.ts +52 -0
  26. package/.next/standalone/src/app/api/wiki/folder/route.ts +26 -0
  27. package/.next/standalone/src/app/api/wiki/move/route.ts +45 -0
  28. package/.next/standalone/src/app/api/wiki/page/route.ts +84 -0
  29. package/.next/standalone/src/app/api/wiki/route.ts +98 -0
  30. package/.next/standalone/src/app/api/wiki/slugs/route.ts +50 -0
  31. package/.next/standalone/src/app/api/wiki/upload/route.ts +87 -0
  32. package/.next/standalone/src/app/api/wiki/watch/route.ts +86 -0
  33. package/.next/standalone/src/app/globals.css +716 -0
  34. package/.next/standalone/src/app/icon.svg +11 -0
  35. package/.next/standalone/src/app/layout.tsx +35 -0
  36. package/.next/standalone/src/app/page.tsx +1509 -0
  37. package/.next/standalone/src/components/confirm-dialog.tsx +56 -0
  38. package/.next/standalone/src/components/dir-picker.tsx +393 -0
  39. package/.next/standalone/src/components/editor/bubble-menu.tsx +286 -0
  40. package/.next/standalone/src/components/editor/callout-extension.ts +36 -0
  41. package/.next/standalone/src/components/editor/csv-viewer.tsx +335 -0
  42. package/.next/standalone/src/components/editor/editor-toolbar.tsx +564 -0
  43. package/.next/standalone/src/components/editor/editor.tsx +633 -0
  44. package/.next/standalone/src/components/editor/extensions/drag-handle.ts +325 -0
  45. package/.next/standalone/src/components/editor/extensions/heading-anchors.ts +55 -0
  46. package/.next/standalone/src/components/editor/extensions/resizable-image.tsx +159 -0
  47. package/.next/standalone/src/components/editor/extensions.ts +148 -0
  48. package/.next/standalone/src/components/editor/file-fallback-viewer.tsx +74 -0
  49. package/.next/standalone/src/components/editor/folder-index.tsx +235 -0
  50. package/.next/standalone/src/components/editor/google-doc-viewer.tsx +73 -0
  51. package/.next/standalone/src/components/editor/image-viewer.tsx +56 -0
  52. package/.next/standalone/src/components/editor/link-popover.tsx +111 -0
  53. package/.next/standalone/src/components/editor/media-popover.tsx +211 -0
  54. package/.next/standalone/src/components/editor/media-viewer.tsx +69 -0
  55. package/.next/standalone/src/components/editor/mermaid-viewer.tsx +285 -0
  56. package/.next/standalone/src/components/editor/node-app-viewer.tsx +269 -0
  57. package/.next/standalone/src/components/editor/notebook-viewer.tsx +370 -0
  58. package/.next/standalone/src/components/editor/office/docx-viewer.tsx +92 -0
  59. package/.next/standalone/src/components/editor/office/office-chrome.tsx +82 -0
  60. package/.next/standalone/src/components/editor/office/pptx-viewer.tsx +103 -0
  61. package/.next/standalone/src/components/editor/office/xlsx-viewer.tsx +116 -0
  62. package/.next/standalone/src/components/editor/pdf-viewer.tsx +31 -0
  63. package/.next/standalone/src/components/editor/slash-commands.tsx +454 -0
  64. package/.next/standalone/src/components/editor/source-viewer.tsx +212 -0
  65. package/.next/standalone/src/components/editor/table-menu.tsx +232 -0
  66. package/.next/standalone/src/components/editor/website-viewer.tsx +74 -0
  67. package/.next/standalone/src/components/editor/wiki-link-create-dialog.tsx +188 -0
  68. package/.next/standalone/src/components/editor/wiki-link-decorator.ts +79 -0
  69. package/.next/standalone/src/components/editor/wiki-link-extension.ts +120 -0
  70. package/.next/standalone/src/components/editor/wiki-link-picker.tsx +250 -0
  71. package/.next/standalone/src/components/layout/viewer-toolbar.tsx +54 -0
  72. package/.next/standalone/src/components/theme-provider.tsx +16 -0
  73. package/.next/standalone/src/components/theme-toggle.tsx +24 -0
  74. package/.next/standalone/src/components/ui/badge.tsx +35 -0
  75. package/.next/standalone/src/components/ui/button.tsx +56 -0
  76. package/.next/standalone/src/components/ui/card.tsx +63 -0
  77. package/.next/standalone/src/components/ui/collapsible.tsx +9 -0
  78. package/.next/standalone/src/components/ui/command.tsx +150 -0
  79. package/.next/standalone/src/components/ui/context-menu.tsx +191 -0
  80. package/.next/standalone/src/components/ui/dialog.tsx +119 -0
  81. package/.next/standalone/src/components/ui/dropdown-menu.tsx +194 -0
  82. package/.next/standalone/src/components/ui/input.tsx +22 -0
  83. package/.next/standalone/src/components/ui/label.tsx +25 -0
  84. package/.next/standalone/src/components/ui/popover.tsx +33 -0
  85. package/.next/standalone/src/components/ui/scroll-area.tsx +47 -0
  86. package/.next/standalone/src/components/ui/select.tsx +156 -0
  87. package/.next/standalone/src/components/ui/separator.tsx +30 -0
  88. package/.next/standalone/src/components/ui/sheet.tsx +140 -0
  89. package/.next/standalone/src/components/ui/skeleton.tsx +15 -0
  90. package/.next/standalone/src/components/ui/switch.tsx +28 -0
  91. package/.next/standalone/src/components/ui/tabs.tsx +54 -0
  92. package/.next/standalone/src/components/ui/textarea.tsx +21 -0
  93. package/.next/standalone/src/components/ui/tip.tsx +23 -0
  94. package/.next/standalone/src/components/ui/toggle-group.tsx +41 -0
  95. package/.next/standalone/src/components/ui/tooltip.tsx +27 -0
  96. package/.next/standalone/src/components/wiki/frontmatter-header.tsx +164 -0
  97. package/.next/standalone/src/lib/app-runner.ts +231 -0
  98. package/.next/standalone/src/lib/cabinets/tree.ts +75 -0
  99. package/.next/standalone/src/lib/config.ts +32 -0
  100. package/.next/standalone/src/lib/embeds/detect.ts +142 -0
  101. package/.next/standalone/src/lib/google/detect.ts +107 -0
  102. package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +84 -0
  103. package/.next/standalone/src/lib/markdown/remark-wikilinks.ts +100 -0
  104. package/.next/standalone/src/lib/markdown/to-html.ts +168 -0
  105. package/.next/standalone/src/lib/markdown/to-markdown.ts +238 -0
  106. package/.next/standalone/src/lib/root-dir.ts +35 -0
  107. package/.next/standalone/src/lib/toast.ts +9 -0
  108. package/.next/standalone/src/lib/utils.ts +6 -0
  109. package/.next/standalone/src/lib/wiki-helpers.ts +36 -0
  110. package/.next/standalone/src/stores/ai-panel-store.ts +23 -0
  111. package/.next/standalone/src/stores/editor-store.ts +244 -0
  112. package/.next/standalone/src/stores/tree-store.ts +52 -0
  113. package/.next/standalone/src/stores/wiki-slugs-store.ts +74 -0
  114. package/.next/standalone/src/types/index.ts +47 -0
  115. package/.next/standalone/tailwind.config.ts +201 -0
  116. package/.next/standalone/tsconfig.json +41 -0
  117. package/README.md +283 -0
  118. package/bin/wiki-viewer.js +158 -0
  119. package/package.json +120 -0
@@ -0,0 +1,283 @@
1
+ <div align="center">
2
+ <img src="public/logo.svg" width="80" height="80" alt="wiki-viewer logo" />
3
+ <h1>wiki-viewer</h1>
4
+ <p><strong>Browse, read, and edit your local files from a clean web UI.</strong></p>
5
+ <p>
6
+ Markdown · PDF · Office docs · Notebooks · Images · Code · and more
7
+ </p>
8
+
9
+ <p>
10
+ <!-- npm badge placeholder — will activate once package is published -->
11
+ <!-- <a href="https://www.npmjs.com/package/wiki-viewer"><img src="https://img.shields.io/npm/v/wiki-viewer" alt="npm version" /></a> -->
12
+ <img src="https://img.shields.io/badge/npm-not%20yet%20published-lightgrey" alt="npm not yet published" />
13
+ <img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node.js ≥18" />
14
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license" />
15
+ </p>
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## What is it?
21
+
22
+ **wiki-viewer** is a zero-config local file browser you run from your terminal. It starts a small web server and lets you navigate, read, and edit any directory on your machine — right from the browser.
23
+
24
+ No cloud. No accounts. No syncing. Your files stay on your machine.
25
+
26
+ ---
27
+
28
+ ## Features
29
+
30
+ | Category | What's included |
31
+ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
32
+ | **File viewers** | Markdown (with frontmatter), PDF, images (PNG / JPG / SVG / WebP), video & audio, CSV (table view), source code (syntax highlighting), DOCX, XLSX, PPTX, Jupyter notebooks, Mermaid diagrams, HTML |
33
+ | **Editor** | Rich TipTap editor for Markdown files |
34
+ | **File ops** | Upload files, create folders, delete, drag-to-move |
35
+ | **Wiki links** | `[[page-name]]` links between Markdown files |
36
+ | **Dark mode** | System-aware, with manual toggle |
37
+ | **HTTPS** | Required for remote access — self-signed cert (OpenSSL) or trusted cert (mkcert); runs as HTTPS proxy in front of the internal HTTP server |
38
+
39
+ ---
40
+
41
+ ## Quick start
42
+
43
+ > **Note:** The npm package is not yet published. Use the [dev setup](#-dev-setup) below to run locally from source. The `npx` commands below will work once the package is on npm.
44
+
45
+ ```bash
46
+ # Point it at a directory
47
+ npx wiki-viewer ~/notes
48
+
49
+ # No directory? Pick one in the browser
50
+ npx wiki-viewer
51
+
52
+ # Running on a remote machine? HTTPS is required (see note below)
53
+ npx wiki-viewer ~/notes --https
54
+ ```
55
+
56
+ Then open **http://localhost:3000** (or **https://localhost:3000** with `--https`).
57
+
58
+ > ⚠️ **Running on a remote host?** The app must be accessed over **HTTPS** — not plain HTTP — or several features will silently break (PDF viewer, service workers, and anything that requires a [secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts)). Use `--https` or put it behind a reverse proxy that handles TLS. Plain HTTP only works correctly on `localhost`.
59
+
60
+ ### Options
61
+
62
+ ```
63
+ wiki-viewer [directory] [options]
64
+
65
+ directory Directory to serve (optional — pick in the browser if omitted)
66
+
67
+ Options:
68
+ -p, --port <port> Port to listen on (default: 3000)
69
+ -H, --host <host> Host to bind to (default: localhost)
70
+ --https Enable HTTPS (self-signed cert, required for service workers on remote)
71
+ -h, --help Show this help message
72
+ ```
73
+
74
+ **Examples:**
75
+
76
+ ```bash
77
+ # Custom port
78
+ npx wiki-viewer ~/notes -p 8080
79
+
80
+ # Bind to all interfaces (accessible on your local network)
81
+ npx wiki-viewer ~/notes -H 0.0.0.0
82
+
83
+ # HTTPS on a custom port
84
+ npx wiki-viewer ~/notes --https -p 8443
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 🔧 Dev setup
90
+
91
+ Follow these steps to run wiki-viewer from source for local development or contribution.
92
+
93
+ ### Prerequisites
94
+
95
+ - **Node.js** ≥ 18 — [nodejs.org](https://nodejs.org)
96
+ - **pnpm** — [pnpm.io/installation](https://pnpm.io/installation)
97
+ ```bash
98
+ npm install -g pnpm
99
+ ```
100
+
101
+ ### 1 · Clone the repo
102
+
103
+ ```bash
104
+ git clone https://github.com/anh-chu/wiki-viewer.git
105
+ cd wiki-viewer
106
+ ```
107
+
108
+ ### 2 · Install dependencies
109
+
110
+ ```bash
111
+ pnpm install
112
+ ```
113
+
114
+ ### 3 · Start the dev server
115
+
116
+ ```bash
117
+ # Serve a directory
118
+ ROOT_DIR=~/notes pnpm dev
119
+
120
+ # No directory — pick one in the browser
121
+ pnpm dev
122
+
123
+ # HTTPS dev mode (uses Next.js experimental HTTPS)
124
+ ROOT_DIR=~/notes pnpm dev:https
125
+ ```
126
+
127
+ Open **http://localhost:3000**.
128
+
129
+ The dev server supports **hot reload** — changes to source files are reflected instantly.
130
+
131
+ ### Available scripts
132
+
133
+ | Command | Description |
134
+ | ---------------- | ---------------------------------------- |
135
+ | `pnpm dev` | Start Next.js development server |
136
+ | `pnpm dev:https` | Start dev server with experimental HTTPS |
137
+ | `pnpm build` | Build production bundle |
138
+ | `pnpm start` | Start production server (after build) |
139
+ | `pnpm wiki` | Run the CLI entry point (after build) |
140
+
141
+ ### Environment variables
142
+
143
+ | Variable | Description | Default |
144
+ | ---------- | ------------------------ | --------------------- |
145
+ | `ROOT_DIR` | Directory to serve | `~/wiki-viewer-files` |
146
+ | `PORT` | Port to listen on | `3000` |
147
+ | `HOSTNAME` | Host / interface to bind | `localhost` |
148
+
149
+ ---
150
+
151
+ ## 🚀 Local deployment (self-hosted)
152
+
153
+ Want to run wiki-viewer as a persistent server on your machine or a home server? Here's how.
154
+
155
+ ### Option A — Build and run directly
156
+
157
+ ```bash
158
+ # 1. Clone & install
159
+ git clone https://github.com/anh-chu/wiki-viewer.git
160
+ cd wiki-viewer
161
+ pnpm install
162
+
163
+ # 2. Build the production bundle
164
+ pnpm build
165
+
166
+ # 3. Copy static assets into the standalone output (required step)
167
+ cp -r .next/static .next/standalone/.next/static
168
+ cp -r public .next/standalone/public
169
+
170
+ # 4. Start the server
171
+ ROOT_DIR=/path/to/your/files node .next/standalone/server.js
172
+ ```
173
+
174
+ Or use the CLI wrapper (which handles the above automatically):
175
+
176
+ ```bash
177
+ node bin/wiki-viewer.js /path/to/your/files
178
+ ```
179
+
180
+ ### Option B — Run with PM2 (auto-restart on crash / reboot)
181
+
182
+ ```bash
183
+ npm install -g pm2
184
+
185
+ # Start and name the process
186
+ pm2 start bin/wiki-viewer.js \
187
+ --name wiki-viewer \
188
+ --node-args="" \
189
+ -- /path/to/your/files --port 3000
190
+
191
+ # Save so it auto-starts on reboot
192
+ pm2 save
193
+ pm2 startup # follow the printed instructions
194
+ ```
195
+
196
+ ### Option C — systemd service (Linux)
197
+
198
+ Create `/etc/systemd/system/wiki-viewer.service`:
199
+
200
+ ```ini
201
+ [Unit]
202
+ Description=wiki-viewer
203
+ After=network.target
204
+
205
+ [Service]
206
+ Type=simple
207
+ User=YOUR_USER
208
+ WorkingDirectory=/home/YOUR_USER/wiki-viewer
209
+ ExecStart=/usr/bin/node bin/wiki-viewer.js /path/to/your/files --port 3000 --host 0.0.0.0
210
+ Restart=on-failure
211
+ Environment=NODE_ENV=production
212
+
213
+ [Install]
214
+ WantedBy=multi-user.target
215
+ ```
216
+
217
+ Then enable and start it:
218
+
219
+ ```bash
220
+ sudo systemctl daemon-reload
221
+ sudo systemctl enable wiki-viewer
222
+ sudo systemctl start wiki-viewer
223
+ sudo systemctl status wiki-viewer
224
+ ```
225
+
226
+ ### HTTPS on a remote server
227
+
228
+ > ⚠️ **HTTPS is required when wiki-viewer is not on `localhost`.** Browsers restrict several APIs (service workers, PDF.js, and other [secure-context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts) features) to HTTPS-only origins. Accessing wiki-viewer over plain HTTP on a remote host will silently break parts of the UI.
229
+
230
+ **How it works under the hood:** `--https` does not make the Next.js server itself speak TLS. Instead it:
231
+
232
+ 1. Starts the Next.js standalone server on a random internal HTTP port (`127.0.0.1:XXXXX`)
233
+ 2. Starts an HTTPS reverse proxy on the user-facing port that forwards all traffic to the internal server
234
+
235
+ So the chain is always: **browser → HTTPS proxy → internal HTTP server**. This is why `--https` is the correct flag and not a plain HTTP URL even in "HTTPS mode".
236
+
237
+ **Certificate generation** (automatic, stored at `~/.wiki-viewer/certs/`):
238
+
239
+ - If **mkcert** is installed → locally-trusted cert (no browser warning)
240
+ - Otherwise → OpenSSL self-signed cert (browser warns once — click through)
241
+
242
+ ```bash
243
+ node bin/wiki-viewer.js /path/to/your/files --https --port 443 --host 0.0.0.0
244
+ ```
245
+
246
+ > **Tip for production:** Use a real reverse proxy (nginx / Caddy) with a proper TLS certificate instead of `--https`. Configure it to proxy to wiki-viewer's plain HTTP port, and wiki-viewer will work correctly behind it — no `--https` flag needed.
247
+
248
+ ---
249
+
250
+ ## Project structure
251
+
252
+ ```
253
+ wiki-viewer/
254
+ ├── bin/
255
+ │ └── wiki-viewer.js CLI entry point
256
+ ├── src/
257
+ │ ├── app/ Next.js app router pages & API routes
258
+ │ ├── components/ React components (viewer, editor, sidebar…)
259
+ │ ├── lib/ File system helpers, parsers, utilities
260
+ │ ├── stores/ Zustand state stores
261
+ │ └── types/ Shared TypeScript types
262
+ ├── public/
263
+ │ └── logo.svg
264
+ ├── next.config.ts
265
+ └── package.json
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Contributing
271
+
272
+ 1. Fork the repo and create a branch: `git checkout -b my-feature`
273
+ 2. Make your changes
274
+ 3. Test with `ROOT_DIR=~/notes pnpm dev`
275
+ 4. Open a pull request
276
+
277
+ Bug reports and feature requests welcome via [GitHub Issues](https://github.com/anh-chu/wiki-viewer/issues).
278
+
279
+ ---
280
+
281
+ ## License
282
+
283
+ MIT © [anh-chu](https://github.com/anh-chu)
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ import { spawn, execSync } from "node:child_process";
3
+ import { createServer as createHttpsServer } from "node:https";
4
+ import { createServer as createHttpServer, request as httpRequest } from "node:http";
5
+ import { createServer as createNetServer } from "node:net";
6
+ import { readFileSync, mkdirSync, existsSync } from "node:fs";
7
+ import path from "node:path";
8
+ import os from "node:os";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const appRoot = path.resolve(__dirname, "..");
13
+ const serverJs = path.join(appRoot, ".next", "standalone", "server.js");
14
+
15
+ function printUsage() {
16
+ console.error("Usage: wiki-viewer [directory] [options]");
17
+ console.error("");
18
+ console.error(" directory Directory to serve (optional — pick in browser if omitted)");
19
+ console.error("");
20
+ console.error("Options:");
21
+ console.error(" -p, --port <port> Port to listen on (default: 3000)");
22
+ console.error(" -H, --host <host> Host to bind to (default: localhost)");
23
+ console.error(" --https Enable HTTPS (self-signed cert, enables service workers)");
24
+ console.error("");
25
+ console.error("Examples:");
26
+ console.error(" wiki-viewer ~/notes");
27
+ console.error(" wiki-viewer ~/notes --https");
28
+ console.error(" wiki-viewer ~/notes -p 8080 -H 0.0.0.0");
29
+ }
30
+
31
+ if (!existsSync(serverJs)) {
32
+ console.error("Error: pre-built server not found at", serverJs);
33
+ console.error("This is a bug – please report it at https://github.com/anh-chu/wiki-viewer/issues");
34
+ process.exit(1);
35
+ }
36
+
37
+ const args = process.argv.slice(2);
38
+
39
+ if (args.includes("--help") || args.includes("-h")) {
40
+ printUsage();
41
+ process.exit(0);
42
+ }
43
+
44
+ const rootDir = args.find((a) => !a.startsWith("-"));
45
+ let port = process.env.PORT ?? "3000";
46
+ let host = process.env.HOSTNAME ?? "localhost";
47
+ let useHttps = false;
48
+
49
+ for (let i = 0; i < args.length; i++) {
50
+ const a = args[i];
51
+ if (a === "-p" || a === "--port") port = args[++i] ?? port;
52
+ else if (a === "-H" || a === "--host") host = args[++i] ?? host;
53
+ else if (a === "--https") useHttps = true;
54
+ }
55
+
56
+ const resolvedRoot = rootDir ? path.resolve(rootDir) : null;
57
+
58
+ // ── HTTPS cert generation ──────────────────────────────────────────────────
59
+
60
+ function ensureCerts() {
61
+ const dir = path.join(os.homedir(), ".wiki-viewer", "certs");
62
+ mkdirSync(dir, { recursive: true });
63
+ const keyPath = path.join(dir, "key.pem");
64
+ const certPath = path.join(dir, "cert.pem");
65
+ if (existsSync(keyPath) && existsSync(certPath)) {
66
+ return { key: readFileSync(keyPath), cert: readFileSync(certPath) };
67
+ }
68
+ // Try mkcert (trusted), fall back to openssl (self-signed)
69
+ try {
70
+ execSync("mkcert -version", { stdio: "ignore" });
71
+ execSync(`mkcert -install 2>/dev/null; mkcert -key-file "${keyPath}" -cert-file "${certPath}" localhost 127.0.0.1 "${host}"`, { stdio: "pipe" });
72
+ console.log("🔒 Trusted cert via mkcert");
73
+ } catch {
74
+ try {
75
+ execSync(
76
+ `openssl req -x509 -newkey rsa:2048 -keyout "${keyPath}" -out "${certPath}" -days 825 -nodes -subj "/CN=localhost"`,
77
+ { stdio: "ignore" },
78
+ );
79
+ console.log("🔒 Self-signed cert (browser will warn once — click through)");
80
+ } catch {
81
+ console.error("Error: --https requires mkcert or openssl");
82
+ process.exit(1);
83
+ }
84
+ }
85
+ return { key: readFileSync(keyPath), cert: readFileSync(certPath) };
86
+ }
87
+
88
+ // ── free port helper ───────────────────────────────────────────────────────
89
+
90
+ function freePort() {
91
+ return new Promise((resolve) => {
92
+ const s = createNetServer();
93
+ s.listen(0, "127.0.0.1", () => {
94
+ const { port: p } = s.address();
95
+ s.close(() => resolve(p));
96
+ });
97
+ });
98
+ }
99
+
100
+ // ── start ──────────────────────────────────────────────────────────────────
101
+
102
+ async function start() {
103
+ if (resolvedRoot) {
104
+ console.log(`📂 ${resolvedRoot}`);
105
+ } else {
106
+ console.log("📂 No directory specified — open the browser to choose one");
107
+ }
108
+
109
+ // When HTTPS is requested, run the standalone server on a random internal
110
+ // HTTP port and stand up an HTTPS reverse-proxy on the user-facing port.
111
+ const internalPort = useHttps ? String(await freePort()) : port;
112
+ const internalHost = "127.0.0.1";
113
+
114
+ const child = spawn(process.execPath, [serverJs], {
115
+ cwd: path.join(appRoot, ".next", "standalone"),
116
+ stdio: "inherit",
117
+ env: {
118
+ ...process.env,
119
+ ...(resolvedRoot ? { ROOT_DIR: resolvedRoot } : {}),
120
+ PORT: internalPort,
121
+ HOSTNAME: internalHost,
122
+ },
123
+ });
124
+
125
+ child.on("exit", (code) => process.exit(code ?? 0));
126
+
127
+ if (useHttps) {
128
+ const { key, cert } = ensureCerts();
129
+
130
+ // Simple HTTPS → HTTP proxy
131
+ const proxy = createHttpsServer({ key, cert }, (req, res) => {
132
+ const options = {
133
+ hostname: internalHost,
134
+ port: Number(internalPort),
135
+ path: req.url,
136
+ method: req.method,
137
+ headers: req.headers,
138
+ };
139
+ const upstream = httpRequest(options, (upRes) => {
140
+ res.writeHead(upRes.statusCode, upRes.headers);
141
+ upRes.pipe(res);
142
+ });
143
+ upstream.on("error", () => res.destroy());
144
+ req.pipe(upstream);
145
+ });
146
+
147
+ // Wait a moment for the standalone server to bind before starting proxy
148
+ setTimeout(() => {
149
+ proxy.listen(Number(port), host, () => {
150
+ console.log(`🌐 https://${host}:${port}`);
151
+ });
152
+ }, 1_000);
153
+ } else {
154
+ console.log(`🌐 http://${host}:${port}`);
155
+ }
156
+ }
157
+
158
+ start();
@@ -0,0 +1,11 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: "standalone",
5
+ experimental: {
6
+ optimizePackageImports: ["lucide-react"],
7
+ },
8
+ allowedDevOrigins: ['devvm']
9
+ };
10
+
11
+ export default nextConfig;
@@ -0,0 +1,120 @@
1
+ {
2
+ "name": "wiki-viewer",
3
+ "version": "1.0.0",
4
+ "description": "Local file viewer and wiki — view markdown, HTML, PDFs, notebooks, office docs, and more from any directory",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "wiki-viewer": "bin/wiki-viewer.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ ".next/standalone"
13
+ ],
14
+ "scripts": {
15
+ "dev": "next dev",
16
+ "dev:https": "next dev --experimental-https",
17
+ "build": "next build",
18
+ "start": "next start",
19
+ "wiki": "node bin/wiki-viewer.js",
20
+ "prepublishOnly": "npm run build && cp -r .next/static .next/standalone/.next/static && cp -r public .next/standalone/public"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ },
25
+ "keywords": [
26
+ "wiki",
27
+ "markdown",
28
+ "viewer",
29
+ "notes",
30
+ "local",
31
+ "cli"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/anh-chu/wiki-viewer.git"
36
+ },
37
+ "dependencies": {
38
+ "chokidar": "^5.0.0",
39
+ "undici": "^8.3.0"
40
+ },
41
+ "devDependencies": {
42
+ "@radix-ui/react-collapsible": "^1.1.12",
43
+ "@radix-ui/react-context-menu": "^2.2.16",
44
+ "@radix-ui/react-dialog": "^1.1.15",
45
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
46
+ "@radix-ui/react-label": "^2.1.8",
47
+ "@radix-ui/react-popover": "^1.1.15",
48
+ "@radix-ui/react-scroll-area": "^1.2.10",
49
+ "@radix-ui/react-select": "^2.2.6",
50
+ "@radix-ui/react-separator": "^1.1.8",
51
+ "@radix-ui/react-slot": "^1.2.4",
52
+ "@radix-ui/react-switch": "^1.2.6",
53
+ "@radix-ui/react-tabs": "^1.1.13",
54
+ "@radix-ui/react-toggle-group": "^1.1.11",
55
+ "@radix-ui/react-tooltip": "^1.2.8",
56
+ "@tailwindcss/typography": "^0.5.19",
57
+ "@tiptap/core": "^3.22.5",
58
+ "@tiptap/extension-bubble-menu": "^3.22.5",
59
+ "@tiptap/extension-code-block-lowlight": "^3.22.5",
60
+ "@tiptap/extension-color": "^3.22.5",
61
+ "@tiptap/extension-image": "^3.22.5",
62
+ "@tiptap/extension-link": "^3.22.5",
63
+ "@tiptap/extension-placeholder": "^3.22.5",
64
+ "@tiptap/extension-subscript": "^3.22.5",
65
+ "@tiptap/extension-superscript": "^3.22.5",
66
+ "@tiptap/extension-table": "^3.22.5",
67
+ "@tiptap/extension-table-cell": "^3.22.5",
68
+ "@tiptap/extension-table-header": "^3.22.5",
69
+ "@tiptap/extension-table-row": "^3.22.5",
70
+ "@tiptap/extension-task-item": "^3.22.5",
71
+ "@tiptap/extension-task-list": "^3.22.5",
72
+ "@tiptap/extension-text-align": "^3.22.5",
73
+ "@tiptap/extension-underline": "^3.22.5",
74
+ "@tiptap/pm": "^3.22.5",
75
+ "@tiptap/react": "^3.22.5",
76
+ "@tiptap/starter-kit": "^3.22.5",
77
+ "@tiptap/suggestion": "^3.22.5",
78
+ "@types/mdast": "^4.0.4",
79
+ "@types/node": "^22.15.0",
80
+ "@types/papaparse": "^5.5.2",
81
+ "@types/react": "19.2.14",
82
+ "@types/react-dom": "19.2.3",
83
+ "@types/turndown": "^5.0.6",
84
+ "autoprefixer": "^10.4.20",
85
+ "class-variance-authority": "^0.7.1",
86
+ "clsx": "^2.1.1",
87
+ "cmdk": "^1.1.1",
88
+ "docx-preview": "^0.3.7",
89
+ "gray-matter": "^4.0.3",
90
+ "hast-util-to-html": "^9.0.5",
91
+ "highlight.js": "^11.11.1",
92
+ "jszip": "^3.10.1",
93
+ "lowlight": "^3.3.0",
94
+ "lucide-react": "^0.525.0",
95
+ "mammoth": "^1.12.0",
96
+ "mermaid": "^11.14.0",
97
+ "next": "16.2.4",
98
+ "next-themes": "^0.4.6",
99
+ "papaparse": "^5.5.3",
100
+ "postcss": "^8.5.6",
101
+ "pptx-preview": "^1.0.7",
102
+ "react": "19.2.5",
103
+ "react-dom": "19.2.5",
104
+ "react-markdown": "^10.1.0",
105
+ "react-pdf": "^10.4.1",
106
+ "rehype-stringify": "^10.0.1",
107
+ "remark-gfm": "^4.0.1",
108
+ "remark-parse": "^11.0.0",
109
+ "remark-rehype": "^11.1.2",
110
+ "sonner": "^2.0.7",
111
+ "tailwind-merge": "^3.3.0",
112
+ "tailwindcss": "^3.4.17",
113
+ "turndown": "^7.2.4",
114
+ "turndown-plugin-gfm": "^1.0.2",
115
+ "typescript": "^5.8.0",
116
+ "unified": "^11.0.5",
117
+ "xlsx": "^0.18.5",
118
+ "zustand": "^5.0.12"
119
+ }
120
+ }