md-lv 1.0.2 → 1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 markdown-viewer contributors
3
+ Copyright (c) 2026 md-lv contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # markdown-viewer
1
+ # md-lv
2
2
 
3
3
  > Serve Markdown files as HTML with live features
4
4
 
@@ -123,8 +123,8 @@ $$
123
123
 
124
124
  ```bash
125
125
  # Clone repository
126
- git clone https://github.com/your-username/markdown-viewer.git
127
- cd markdown-viewer
126
+ git clone https://github.com/no-problem-dev/markdown-live-viewer.git
127
+ cd markdown-live-viewer
128
128
 
129
129
  # Install dependencies
130
130
  npm install
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md-lv",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Serve Markdown files as HTML with live features - syntax highlighting, Mermaid diagrams, and MathJax formulas",
5
5
  "type": "module",
6
6
  "engines": {
@@ -45,12 +45,12 @@
45
45
  "license": "MIT",
46
46
  "repository": {
47
47
  "type": "git",
48
- "url": "git+https://github.com/no-problem-dev/markdown-viewer.git"
48
+ "url": "git+https://github.com/no-problem-dev/markdown-live-viewer.git"
49
49
  },
50
50
  "bugs": {
51
- "url": "https://github.com/no-problem-dev/markdown-viewer/issues"
51
+ "url": "https://github.com/no-problem-dev/markdown-live-viewer/issues"
52
52
  },
53
- "homepage": "https://github.com/no-problem-dev/markdown-viewer#readme",
53
+ "homepage": "https://github.com/no-problem-dev/markdown-live-viewer#readme",
54
54
  "dependencies": {
55
55
  "express": "^5.0.0",
56
56
  "commander": "^12.0.0",
package/public/js/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * markdown-viewer Client-side JavaScript
2
+ * md-lv Client-side JavaScript
3
3
  */
4
4
 
5
5
  document.addEventListener('DOMContentLoaded', async () => {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * markdown-viewer Navigation Enhancement
2
+ * md-lv Navigation Enhancement
3
3
  * Keyboard navigation and UI improvements
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * markdown-viewer Search Functionality
2
+ * md-lv Search Functionality
3
3
  */
4
4
 
5
5
  document.addEventListener('DOMContentLoaded', () => {
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- markdown-viewer Base Styles
2
+ md-lv Base Styles
3
3
  GitHub-inspired Markdown styling
4
4
  ========================================================================== */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- markdown-viewer Modern Styles
2
+ md-lv Modern Styles
3
3
  Dark mode support and enhanced UI
4
4
  ========================================================================== */
5
5
 
package/src/routes/api.js CHANGED
@@ -22,9 +22,6 @@ async function getAllFiles(dir, baseDir, maxDepth = 5, currentDepth = 0) {
22
22
  const entries = await fs.readdir(dir, { withFileTypes: true });
23
23
 
24
24
  for (const entry of entries) {
25
- // 隠しファイル・ディレクトリをスキップ
26
- if (entry.name.startsWith('.')) continue;
27
-
28
25
  // node_modules をスキップ
29
26
  if (entry.name === 'node_modules') continue;
30
27
 
@@ -0,0 +1,65 @@
1
+ import { Router } from 'express';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { validatePath } from '../utils/path.js';
5
+
6
+ const router = Router();
7
+
8
+ /**
9
+ * 画像ファイルの MIME タイプマップ
10
+ */
11
+ const IMAGE_MIME_TYPES = {
12
+ '.png': 'image/png',
13
+ '.jpg': 'image/jpeg',
14
+ '.jpeg': 'image/jpeg',
15
+ '.gif': 'image/gif',
16
+ '.svg': 'image/svg+xml',
17
+ '.webp': 'image/webp',
18
+ '.ico': 'image/x-icon',
19
+ '.bmp': 'image/bmp',
20
+ '.avif': 'image/avif'
21
+ };
22
+
23
+ /**
24
+ * 画像ファイル配信ルート
25
+ * Markdownから相対パスで参照される画像を配信
26
+ */
27
+ router.get(/^\/.*/, async (req, res, next) => {
28
+ try {
29
+ const requestPath = req.path;
30
+ const ext = path.extname(requestPath).toLowerCase();
31
+
32
+ // 画像ファイル以外は次のハンドラへ
33
+ const mimeType = IMAGE_MIME_TYPES[ext];
34
+ if (!mimeType) {
35
+ return next();
36
+ }
37
+
38
+ const docRoot = req.app.get('docRoot');
39
+ let filePath;
40
+
41
+ try {
42
+ filePath = validatePath(requestPath, docRoot);
43
+ } catch (error) {
44
+ if (error.code === 'ETRAVERSAL') {
45
+ return res.status(403).send('Access denied');
46
+ }
47
+ throw error;
48
+ }
49
+
50
+ // ファイル存在確認
51
+ try {
52
+ await fs.access(filePath);
53
+ } catch {
54
+ return next(); // 存在しない場合は次のハンドラへ
55
+ }
56
+
57
+ // 画像ファイルを配信
58
+ const imageData = await fs.readFile(filePath);
59
+ res.type(mimeType).send(imageData);
60
+ } catch (error) {
61
+ next(error);
62
+ }
63
+ });
64
+
65
+ export default router;
@@ -73,11 +73,8 @@ router.get(/^\/.*/, async (req, res, next) => {
73
73
  return a.name.localeCompare(b.name);
74
74
  });
75
75
 
76
- // 隠しファイルをフィルタ(オプション)
77
- const visible = sorted.filter(entry => !entry.name.startsWith('.'));
78
-
79
76
  // HTML リスト生成
80
- const listHtml = visible.map(entry => {
77
+ const listHtml = sorted.map(entry => {
81
78
  const isDir = entry.isDirectory;
82
79
  const href = path.join(requestPath, entry.name) + (isDir ? '/' : '');
83
80
  const iconClass = getIconClass(entry.name, isDir);
@@ -94,8 +91,9 @@ router.get(/^\/.*/, async (req, res, next) => {
94
91
  ? `<li class="folder parent"><a href="${path.dirname(requestPath)}/">..</a><span class="size">-</span></li>\n`
95
92
  : '';
96
93
 
94
+ const docRootName = req.app.get('docRootName');
97
95
  const html = renderTemplate('page', {
98
- title: `Index of ${requestPath}`,
96
+ title: `${requestPath === '/' ? docRootName : requestPath} - ${docRootName}`,
99
97
  content: `<h1>Index of ${escapeHtml(requestPath)}</h1>
100
98
  <ul class="directory-listing">${parentLink}${listHtml}</ul>`,
101
99
  breadcrumbs: generateBreadcrumbs(requestPath)
@@ -31,8 +31,9 @@ router.get(/.*\.md$/i, async (req, res, next) => {
31
31
  const content = await fs.readFile(filePath, 'utf-8');
32
32
 
33
33
  // テンプレートにMarkdownを埋め込み(クライアント側でレンダリング)
34
+ const docRootName = req.app.get('docRootName');
34
35
  const html = renderTemplate('page', {
35
- title: path.basename(filePath),
36
+ title: `${path.basename(filePath)} - ${docRootName}`,
36
37
  content: `<div id="markdown-source" style="display:none">${escapeHtml(content)}</div>
37
38
  <div id="markdown-rendered"></div>`,
38
39
  breadcrumbs: generateBreadcrumbs(requestPath)
package/src/routes/raw.js CHANGED
@@ -58,8 +58,9 @@ router.get(/^\/.*/, async (req, res, next) => {
58
58
  const language = getLanguageFromExtension(ext);
59
59
  const fileName = path.basename(filePath);
60
60
 
61
+ const docRootName = req.app.get('docRootName');
61
62
  const html = renderTemplate('page', {
62
- title: fileName,
63
+ title: `${fileName} - ${docRootName}`,
63
64
  content: `<h1>${escapeHtml(fileName)}</h1>
64
65
  <pre><code class="language-${language}">${escapeHtml(content)}</code></pre>`,
65
66
  breadcrumbs: generateBreadcrumbs(requestPath)
package/src/server.js CHANGED
@@ -3,6 +3,7 @@ import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import markdownRouter from './routes/markdown.js';
5
5
  import rawRouter from './routes/raw.js';
6
+ import assetsRouter from './routes/assets.js';
6
7
  import directoryRouter from './routes/directory.js';
7
8
  import apiRouter from './routes/api.js';
8
9
  import { securityHeaders, pathTraversalGuard } from './middleware/security.js';
@@ -23,6 +24,7 @@ export function createServer(options = {}) {
23
24
  // ドキュメントルートの設定
24
25
  const docRoot = path.resolve(options.dir || '.');
25
26
  app.set('docRoot', docRoot);
27
+ app.set('docRootName', path.basename(docRoot));
26
28
 
27
29
  // セキュリティミドルウェア(最初に登録)
28
30
  app.use(securityHeaders);
@@ -50,6 +52,9 @@ export function createServer(options = {}) {
50
52
  // Raw コード表示ルート(.js, .py 等)
51
53
  app.use(rawRouter);
52
54
 
55
+ // 画像ファイル配信ルート
56
+ app.use(assetsRouter);
57
+
53
58
  // ディレクトリ一覧ルート
54
59
  app.use(directoryRouter);
55
60
 
@@ -42,7 +42,7 @@
42
42
  </main>
43
43
  <footer>
44
44
  <hr>
45
- Served by <a href="https://www.npmjs.com/package/markdown-viewer">markdown-viewer</a>
45
+ Served by <a href="https://www.npmjs.com/package/md-lv">md-lv</a>
46
46
  </footer>
47
47
 
48
48
  <!-- クライアント側ライブラリ -->