living-documentation 1.1.0 → 1.4.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 CHANGED
@@ -11,11 +11,14 @@ No cloud, no database, no build step — just point it at a folder of `.md` file
11
11
  ## Features
12
12
 
13
13
  - **Sidebar** grouped by category, sorted by date (newest first)
14
+ - **General section** — always first, always expanded; holds uncategorized docs and extra files
15
+ - **Extra files** — include Markdown files from outside the docs folder (e.g. `README.md`, `CLAUDE.md`)
14
16
  - **Full-text search** — instant filter + server-side content search
15
17
  - **Dark mode** — follows system preference, manually toggleable
18
+ - **Syntax highlighting** — always dark, high-contrast code blocks
16
19
  - **Export to PDF** — print-friendly layout via `window.print()`
17
20
  - **Deep links** — share a direct URL to any document (`?doc=…`)
18
- - **Admin panel** — configure title, theme, filename pattern in the browser
21
+ - **Admin panel** — configure title, theme, filename pattern, and extra files in the browser
19
22
  - **Zero frontend build** — Tailwind and highlight.js loaded from CDN
20
23
 
21
24
  ---
@@ -51,7 +54,7 @@ living-documentation ./docs
51
54
  git clone <repo>
52
55
  cd living-documentation
53
56
  npm install
54
- npm run dev -- ./docs # runs via ts-node, no build needed
57
+ npm run dev -- ./docs # nodemon + ts-node, auto-restarts on changes
55
58
  ```
56
59
 
57
60
  ---
@@ -103,7 +106,7 @@ YYYY_MM_DD_[Category]_title_words.md
103
106
  2023_11_03_[Backend]_api_versioning_strategy.md
104
107
  ```
105
108
 
106
- Files that don't match the pattern are still shown — they appear under **Uncategorized** with the filename as the title.
109
+ Files that don't match the pattern are still shown — they appear under **General** with the filename as the title.
107
110
 
108
111
  ---
109
112
 
@@ -117,12 +120,28 @@ A `.living-doc.json` file is created automatically in your docs folder on first
117
120
  "filenamePattern": "YYYY_MM_DD_[Category]_title",
118
121
  "title": "Living Documentation",
119
122
  "theme": "system",
120
- "port": 4321
123
+ "port": 4321,
124
+ "extraFiles": []
121
125
  }
122
126
  ```
123
127
 
124
128
  You can edit it manually or use the **Admin panel** at [http://localhost:4321/admin](http://localhost:4321/admin).
125
129
 
130
+ ### Extra files
131
+
132
+ The `extraFiles` field accepts an ordered list of absolute paths to `.md` files located **outside** the docs folder. These files always appear in the **General** section, before regular General documents, in the order defined.
133
+
134
+ ```json
135
+ {
136
+ "extraFiles": [
137
+ "/path/to/project/README.md",
138
+ "/path/to/project/CLAUDE.md"
139
+ ]
140
+ }
141
+ ```
142
+
143
+ Use the Admin panel's **General — Extra Files** section to browse the filesystem and manage this list without editing the config manually.
144
+
126
145
  ---
127
146
 
128
147
  ## Project structure
@@ -135,7 +154,8 @@ living-documentation/
135
154
  │ ├── server.ts Express app
136
155
  │ ├── routes/
137
156
  │ │ ├── documents.ts Documents API
138
- │ │ └── config.ts Config API
157
+ │ │ ├── config.ts Config API
158
+ │ │ └── browse.ts Filesystem browser API
139
159
  │ ├── lib/
140
160
  │ │ ├── parser.ts Filename parser
141
161
  │ │ └── config.ts Config management
@@ -152,13 +172,14 @@ living-documentation/
152
172
 
153
173
  ## API reference
154
174
 
155
- | Method | Endpoint | Description |
156
- | ------ | -------------------------- | --------------------------------------------------- |
157
- | `GET` | `/api/documents` | List all documents with metadata |
158
- | `GET` | `/api/documents/:id` | Get document content + rendered HTML |
159
- | `GET` | `/api/documents/search?q=` | Full-text search |
160
- | `GET` | `/api/config` | Read config |
161
- | `PUT` | `/api/config` | Update config (`title`, `theme`, `filenamePattern`) |
175
+ | Method | Endpoint | Description |
176
+ | ------ | -------------------------- | ------------------------------------------------------------------ |
177
+ | `GET` | `/api/documents` | List all documents with metadata (includes extra files) |
178
+ | `GET` | `/api/documents/:id` | Get document content + rendered HTML |
179
+ | `GET` | `/api/documents/search?q=` | Full-text search |
180
+ | `GET` | `/api/config` | Read config |
181
+ | `PUT` | `/api/config` | Update config (`title`, `theme`, `filenamePattern`, `extraFiles`) |
182
+ | `GET` | `/api/browse?path=` | List directories and `.md` files at a given filesystem path |
162
183
 
163
184
  ---
164
185
 
@@ -11,19 +11,6 @@
11
11
  tailwind.config = { darkMode: "class", theme: { extend: {} } };
12
12
  </script>
13
13
 
14
- <style>
15
- .field-label {
16
- @apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1;
17
- }
18
- .field-input {
19
- @apply w-full px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700
20
- bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100
21
- placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm;
22
- }
23
- .field-hint {
24
- @apply mt-1 text-xs text-gray-400 dark:text-gray-500;
25
- }
26
- </style>
27
14
  </head>
28
15
 
29
16
  <body
@@ -61,195 +48,124 @@
61
48
  </header>
62
49
 
63
50
  <!-- ── Page ── -->
64
- <main class="max-w-2xl mx-auto px-6 py-10">
65
- <!-- Title -->
66
- <div class="mb-8">
67
- <h2 class="text-xl font-bold text-gray-900 dark:text-gray-50">
68
- Configuration
69
- </h2>
70
- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
71
- Settings are saved to
72
- <code
73
- class="text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded"
74
- >.living-doc.json</code
75
- >
76
- in your docs folder.
77
- </p>
78
- </div>
51
+ <main class="max-w-6xl mx-auto px-6 py-10">
79
52
 
80
- <!-- ── Form ── -->
81
- <form id="config-form" class="space-y-6" novalidate>
82
- <!-- Read-only info card -->
83
- <div
84
- class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5"
85
- >
86
- <h3
87
- class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-4"
88
- >
89
- Server Info
90
- </h3>
91
- <dl class="grid grid-cols-[1fr_auto] gap-x-6 gap-y-3 text-sm">
92
- <div>
93
- <dt class="text-xs text-gray-400 uppercase tracking-wide mb-0.5">
94
- Docs Folder
95
- </dt>
96
- <dd
97
- id="info-folder"
98
- class="font-mono text-xs text-gray-700 dark:text-gray-300 break-all"
99
- >
100
-
101
- </dd>
102
- </div>
103
- <div>
104
- <dt class="text-xs text-gray-400 uppercase tracking-wide mb-0.5">
105
- Port
106
- </dt>
107
- <dd
108
- id="info-port"
109
- class="font-mono text-xs text-gray-700 dark:text-gray-300"
110
- >
111
-
112
- </dd>
113
- </div>
114
- </dl>
115
- </div>
53
+ <!-- ── Two-column layout ── -->
54
+ <form id="config-form" novalidate>
116
55
 
117
- <!-- Extra Files -->
118
- <div
119
- class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5 space-y-4"
120
- >
56
+ <!-- Title + Save on same row -->
57
+ <div class="flex items-center justify-between mb-8">
121
58
  <div>
122
- <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">
123
- General Extra Files
124
- </h3>
125
- <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
126
- Add Markdown files from outside the docs folder to the General
127
- section.
59
+ <h2 class="text-xl font-bold text-gray-900 dark:text-gray-50">Configuration</h2>
60
+ <p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
61
+ Settings are saved to
62
+ <code class="text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">.living-doc.json</code>
63
+ in your docs folder.
128
64
  </p>
129
65
  </div>
130
-
131
- <!-- File browser -->
132
- <div
133
- class="rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden"
134
- >
135
- <div
136
- class="flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"
137
- >
138
- <button
139
- id="browse-up"
140
- onclick="browseUp()"
141
- class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
142
- >
143
- &#8593; Up
144
- </button>
145
- <span
146
- id="browse-path"
147
- class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
148
- ></span>
149
- </div>
150
- <div
151
- id="browse-list"
152
- class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"
153
- ></div>
154
- </div>
155
-
156
- <!-- Added files -->
157
- <div>
158
- <p
159
- class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2"
160
- >
161
- Added files
162
- </p>
163
- <div id="extra-files-list" class="space-y-1"></div>
164
- <p id="extra-files-empty" class="text-xs text-gray-400 italic">
165
- No extra files added yet.
166
- </p>
66
+ <div class="flex items-center gap-4 shrink-0">
67
+ <div id="save-msg" class="text-sm"></div>
68
+ <button type="submit"
69
+ class="px-5 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-950">
70
+ Save changes
71
+ </button>
167
72
  </div>
168
73
  </div>
169
74
 
170
- <!-- Editable settings -->
171
- <div
172
- class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5 space-y-5"
173
- >
174
- <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">
175
- Appearance & Metadata
176
- </h3>
75
+ <div class="grid grid-cols-2 gap-8 items-start">
76
+
77
+ <!-- ── Left column ── -->
78
+ <div class="space-y-6">
79
+
80
+ <!-- Server Info -->
81
+ <div class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5">
82
+ <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-4">Server Info</h3>
83
+ <dl class="grid grid-cols-[1fr_auto] gap-x-6 gap-y-3 text-sm">
84
+ <div>
85
+ <dt class="text-xs text-gray-400 uppercase tracking-wide mb-0.5">Docs Folder</dt>
86
+ <dd id="info-folder" class="font-mono text-xs text-gray-700 dark:text-gray-300 break-all">—</dd>
87
+ </div>
88
+ <div>
89
+ <dt class="text-xs text-gray-400 uppercase tracking-wide mb-0.5">Port</dt>
90
+ <dd id="info-port" class="font-mono text-xs text-gray-700 dark:text-gray-300">—</dd>
91
+ </div>
92
+ </dl>
93
+ </div>
177
94
 
178
- <!-- Site Title -->
179
- <div>
180
- <label class="field-label" for="field-title">Site Title</label>
181
- <input
182
- id="field-title"
183
- name="title"
184
- type="text"
185
- class="field-input"
186
- placeholder="Living Documentation"
187
- />
188
- <p class="field-hint">
189
- Displayed in the browser tab and sidebar header.
190
- </p>
191
- </div>
95
+ <!-- Extra Files -->
96
+ <div class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5 space-y-4">
97
+ <div>
98
+ <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">General — Extra Files</h3>
99
+ <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Add Markdown files from outside the docs folder to the General section.</p>
100
+ </div>
101
+
102
+ <!-- File browser -->
103
+ <div class="rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
104
+ <div class="flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
105
+ <button id="browse-up" onclick="browseUp()"
106
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0">
107
+ &#8593; Up
108
+ </button>
109
+ <span id="browse-path" class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"></span>
110
+ </div>
111
+ <div id="browse-list" class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"></div>
112
+ </div>
113
+
114
+ <!-- Added files -->
115
+ <div>
116
+ <p class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2">Added files</p>
117
+ <div id="extra-files-list" class="space-y-1"></div>
118
+ <p id="extra-files-empty" class="text-xs text-gray-400 italic">No extra files added yet.</p>
119
+ </div>
120
+ </div>
192
121
 
193
- <!-- Theme -->
194
- <div>
195
- <label class="field-label" for="field-theme">Default Theme</label>
196
- <select id="field-theme" name="theme" class="field-input">
197
- <option value="system">System (follow OS preference)</option>
198
- <option value="light">Light</option>
199
- <option value="dark">Dark</option>
200
- </select>
201
- <p class="field-hint">
202
- Users can always override this with the toggle button.
203
- </p>
204
122
  </div>
205
123
 
206
- <!-- Filename Pattern -->
207
- <div>
208
- <label class="field-label" for="field-pattern"
209
- >Filename Pattern</label
210
- >
211
- <input
212
- id="field-pattern"
213
- name="filenamePattern"
214
- type="text"
215
- class="field-input"
216
- placeholder="YYYY_MM_DD_[Category]_title"
217
- />
218
- <p class="field-hint">
219
- Documents matching this pattern are parsed for date, category, and
220
- title.
221
- <span class="font-mono bg-gray-100 dark:bg-gray-800 px-1 rounded"
222
- >YYYY_MM_DD_[Category]_title.md</span
223
- >
224
- </p>
225
- </div>
226
- </div>
124
+ <!-- ── Right column ── -->
125
+ <div class="space-y-6">
126
+
127
+ <!-- Appearance & Metadata -->
128
+ <div class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 divide-y divide-gray-100 dark:divide-gray-800">
129
+ <div class="px-5 py-4">
130
+ <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">Appearance & Metadata</h3>
131
+ </div>
132
+ <div class="px-5 py-4 space-y-1.5">
133
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="field-title">Site Title</label>
134
+ <input id="field-title" name="title" type="text"
135
+ class="w-full px-3 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
136
+ placeholder="Living Documentation" />
137
+ <p class="text-xs text-gray-400 dark:text-gray-500">Displayed in the browser tab and sidebar header.</p>
138
+ </div>
139
+ <div class="px-5 py-4 space-y-1.5">
140
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="field-theme">Default Theme</label>
141
+ <select id="field-theme" name="theme"
142
+ class="w-full px-3 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500">
143
+ <option value="system">System (follow OS preference)</option>
144
+ <option value="light">Light</option>
145
+ <option value="dark">Dark</option>
146
+ </select>
147
+ <p class="text-xs text-gray-400 dark:text-gray-500">Users can always override with the toggle button.</p>
148
+ </div>
149
+ <div class="px-5 py-4 space-y-1.5">
150
+ <label class="block text-sm font-medium text-gray-700 dark:text-gray-300" for="field-pattern">Filename Pattern</label>
151
+ <input id="field-pattern" name="filenamePattern" type="text"
152
+ class="w-full px-3 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
153
+ placeholder="YYYY_MM_DD_[Category]_title" />
154
+ <p class="text-xs text-gray-400 dark:text-gray-500">
155
+ Parsed for date, category, and title.
156
+ <span id="pattern-desc-example" class="font-mono bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-1 rounded">YYYY_MM_DD_[Category]_title.md</span>
157
+ </p>
158
+ </div>
159
+ </div>
227
160
 
228
- <!-- Pattern preview -->
229
- <div
230
- id="pattern-preview"
231
- class="rounded-xl border border-blue-100 dark:border-blue-900 bg-blue-50 dark:bg-blue-950/30 p-5"
232
- >
233
- <h3
234
- class="text-sm font-semibold text-blue-700 dark:text-blue-400 mb-3"
235
- >
236
- Pattern Preview
237
- </h3>
238
- <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
239
- How your pattern parses example filenames:
240
- </p>
241
- <div id="preview-rows" class="space-y-2 text-sm"></div>
242
- </div>
161
+ <!-- Pattern Preview -->
162
+ <div id="pattern-preview" class="rounded-xl border border-blue-100 dark:border-blue-900 bg-blue-50 dark:bg-blue-950/30 p-5">
163
+ <h3 class="text-sm font-semibold text-blue-700 dark:text-blue-400 mb-3">Pattern Preview</h3>
164
+ <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">How your pattern parses example filenames:</p>
165
+ <div id="preview-rows" class="space-y-2 text-sm"></div>
166
+ </div>
243
167
 
244
- <!-- Submit -->
245
- <div class="flex items-center justify-between">
246
- <div id="save-msg" class="text-sm"></div>
247
- <button
248
- type="submit"
249
- class="px-5 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
250
- >
251
- Save changes
252
- </button>
168
+ </div>
253
169
  </div>
254
170
  </form>
255
171
  </main>
@@ -304,12 +220,22 @@
304
220
 
305
221
  async function saveConfig(e) {
306
222
  e.preventDefault();
223
+ const filenamePattern = document.getElementById("field-pattern").value.trim();
224
+ if (filenamePattern) {
225
+ const catCount = (filenamePattern.match(/\[Category\]/gi) || []).length;
226
+ if (catCount === 0) {
227
+ showMsg("Filename pattern must contain [Category]", "error");
228
+ return;
229
+ }
230
+ if (catCount > 1) {
231
+ showMsg("Filename pattern must contain [Category] exactly once", "error");
232
+ return;
233
+ }
234
+ }
307
235
  const payload = {
308
236
  title: document.getElementById("field-title").value.trim(),
309
237
  theme: document.getElementById("field-theme").value,
310
- filenamePattern: document
311
- .getElementById("field-pattern")
312
- .value.trim(),
238
+ filenamePattern,
313
239
  };
314
240
 
315
241
  try {
@@ -347,29 +273,48 @@
347
273
  "readme.md",
348
274
  ];
349
275
 
350
- // Parse like server does
351
- const FULL_PAT = /^(\d{4}_\d{2}_\d{2})_\[([^\]]+)\]_(.+)\.md$/i;
352
- const DATE_ONLY = /^(\d{4}_\d{2}_\d{2})_(.+)\.md$/i;
353
-
354
- function parsePreview(filename) {
355
- const full = filename.match(FULL_PAT);
356
- if (full) {
357
- const [, d, cat, t] = full;
358
- return {
359
- date: d.replace(/_/g, "-"),
360
- category: cat,
361
- title: titleCase(t),
362
- match: true,
363
- };
276
+ function buildPatternsFromFormat(patternStr) {
277
+ if (!patternStr) patternStr = "YYYY_MM_DD_[Category]_title";
278
+ const hasDate = /YYYY.*MM.*DD/.test(patternStr);
279
+ const hasCategory = /\[Category\]/i.test(patternStr);
280
+ const dateGroup = "(\\d{4}_\\d{2}_\\d{2})";
281
+ const catGroup = "\\[([^\\]]+)\\]";
282
+ const catBeforeDate =
283
+ hasDate && hasCategory &&
284
+ patternStr.search(/\[Category\]/i) < patternStr.search(/YYYY/i);
285
+ let full = null, dateOnly = null;
286
+ if (hasDate && hasCategory) {
287
+ const ordered = catBeforeDate
288
+ ? catGroup + "_" + dateGroup
289
+ : dateGroup + "_" + catGroup;
290
+ full = new RegExp("^" + ordered + "_(.+)\\.md$", "i");
291
+ dateOnly = new RegExp("^" + dateGroup + "_(.+)\\.md$", "i");
292
+ } else if (hasDate) {
293
+ dateOnly = new RegExp("^" + dateGroup + "_(.+)\\.md$", "i");
294
+ } else if (hasCategory) {
295
+ full = new RegExp("^" + catGroup + "_(.+)\\.md$", "i");
364
296
  }
365
- const d = filename.match(DATE_ONLY);
366
- if (d) {
367
- return {
368
- date: d[1].replace(/_/g, "-"),
369
- category: "General",
370
- title: titleCase(d[2]),
371
- match: true,
372
- };
297
+ return { full, dateOnly, hasDate, hasCategory, catBeforeDate };
298
+ }
299
+
300
+ function parsePreview(filename, patterns) {
301
+ if (patterns.full) {
302
+ const m = filename.match(patterns.full);
303
+ if (m) {
304
+ if (patterns.hasDate && patterns.hasCategory) {
305
+ const dateStr = patterns.catBeforeDate ? m[2] : m[1];
306
+ const category = patterns.catBeforeDate ? m[1] : m[2];
307
+ return { date: dateStr.replace(/_/g, "-"), category, title: titleCase(m[3]), match: true };
308
+ } else if (patterns.hasCategory) {
309
+ return { date: null, category: m[1], title: titleCase(m[2]), match: true };
310
+ }
311
+ }
312
+ }
313
+ if (patterns.dateOnly) {
314
+ const m = filename.match(patterns.dateOnly);
315
+ if (m) {
316
+ return { date: m[1].replace(/_/g, "-"), category: "General", title: titleCase(m[2]), match: true };
317
+ }
373
318
  }
374
319
  return {
375
320
  date: null,
@@ -387,9 +332,13 @@
387
332
  }
388
333
 
389
334
  function updatePreview() {
335
+ const patternVal = document.getElementById("field-pattern").value.trim();
336
+ const descSpan = document.getElementById("pattern-desc-example");
337
+ descSpan.textContent = (patternVal || "YYYY_MM_DD_[Category]_title") + ".md";
338
+ const patterns = buildPatternsFromFormat(patternVal);
390
339
  const rows = document.getElementById("preview-rows");
391
340
  rows.innerHTML = EXAMPLES.map((f) => {
392
- const p = parsePreview(f);
341
+ const p = parsePreview(f, patterns);
393
342
  return `
394
343
  <div class="rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-3">
395
344
  <p class="font-mono text-xs text-gray-500 dark:text-gray-400 mb-2 break-all">${esc(f)}</p>
@@ -151,7 +151,7 @@
151
151
  <p class="text-sm text-gray-500 dark:text-gray-500">
152
152
  Choose a document from the sidebar to start reading.
153
153
  </p>
154
- <p class="mt-4 text-xs text-gray-400 dark:text-gray-600 font-mono bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 inline-block">
154
+ <p id="welcome-pattern" class="mt-4 text-xs text-gray-400 dark:text-gray-600 font-mono bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 inline-block">
155
155
  YYYY_MM_DD_[Category]_title.md
156
156
  </p>
157
157
  <p class="mt-2 text-xs text-gray-400">Expected filename pattern</p>
@@ -222,10 +222,15 @@ document.addEventListener('DOMContentLoaded', async () => {
222
222
  await loadConfig();
223
223
  await loadDocuments();
224
224
 
225
- // Deep-link via ?doc=id
225
+ // Deep-link via ?doc=id, otherwise open first General doc
226
226
  const params = new URLSearchParams(location.search);
227
227
  const docId = params.get('doc');
228
- if (docId) openDocument(docId, true);
228
+ if (docId) {
229
+ openDocument(docId, true);
230
+ } else {
231
+ const first = allDocs.find(d => d.category === 'General') ?? allDocs[0];
232
+ if (first) openDocument(first.id, true);
233
+ }
229
234
  });
230
235
 
231
236
  // ── Dark mode ──────────────────────────────────────────────
@@ -254,6 +259,9 @@ async function loadConfig() {
254
259
  const cfg = await fetch('/api/config').then(r => r.json());
255
260
  if (cfg.title) document.title = cfg.title;
256
261
  document.getElementById('app-title').textContent = cfg.title || 'Living Documentation';
262
+ if (cfg.filenamePattern) {
263
+ document.getElementById('welcome-pattern').textContent = cfg.filenamePattern + '.md';
264
+ }
257
265
  } catch { /* non-fatal */ }
258
266
  }
259
267
 
@@ -6,5 +6,5 @@ export interface DocMetadata {
6
6
  date: string | null;
7
7
  formattedDate: string | null;
8
8
  }
9
- export declare function parseFilename(filename: string): DocMetadata;
9
+ export declare function parseFilename(filename: string, filenamePattern?: string): DocMetadata;
10
10
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/lib/parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA0BD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAyC3D"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/lib/parser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAkED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,WAAW,CAwCrF"}
@@ -1,10 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseFilename = parseFilename;
4
- // Matches: YYYY_MM_DD_[Category]_title_words.md
5
- const FULL_PATTERN = /^(\d{4}_\d{2}_\d{2})_\[([^\]]+)\]_(.+)\.md$/i;
6
- // Matches: YYYY_MM_DD_title_words.md (no category)
7
- const DATE_ONLY_PATTERN = /^(\d{4}_\d{2}_\d{2})_(.+)\.md$/i;
4
+ function buildPatternsFromFormat(patternStr) {
5
+ const hasDate = /YYYY.*MM.*DD/.test(patternStr);
6
+ const hasCategory = /\[Category\]/i.test(patternStr);
7
+ const dateGroup = '(\\d{4}_\\d{2}_\\d{2})';
8
+ const catGroup = '\\[([^\\]]+)\\]';
9
+ const catBeforeDate = hasDate && hasCategory &&
10
+ patternStr.search(/\[Category\]/i) < patternStr.search(/YYYY/i);
11
+ if (hasDate && hasCategory) {
12
+ const ordered = catBeforeDate
13
+ ? `${catGroup}_${dateGroup}`
14
+ : `${dateGroup}_${catGroup}`;
15
+ return {
16
+ full: new RegExp(`^${ordered}_(.+)\\.md$`, 'i'),
17
+ dateOnly: new RegExp(`^${dateGroup}_(.+)\\.md$`, 'i'),
18
+ hasDate,
19
+ hasCategory,
20
+ catBeforeDate,
21
+ };
22
+ }
23
+ else if (hasDate) {
24
+ return {
25
+ full: null,
26
+ dateOnly: new RegExp(`^${dateGroup}_(.+)\\.md$`, 'i'),
27
+ hasDate,
28
+ hasCategory,
29
+ catBeforeDate: false,
30
+ };
31
+ }
32
+ else if (hasCategory) {
33
+ return {
34
+ full: new RegExp(`^${catGroup}_(.+)\\.md$`, 'i'),
35
+ dateOnly: null,
36
+ hasDate,
37
+ hasCategory,
38
+ catBeforeDate: false,
39
+ };
40
+ }
41
+ return { full: null, dateOnly: null, hasDate, hasCategory, catBeforeDate: false };
42
+ }
8
43
  function formatDate(iso) {
9
44
  const [year, month, day] = iso.split('-').map(Number);
10
45
  const d = new Date(Date.UTC(year, month - 1, day));
@@ -21,33 +56,32 @@ function titleCase(raw) {
21
56
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
22
57
  .join(' ');
23
58
  }
24
- function parseFilename(filename) {
59
+ function parseFilename(filename, filenamePattern) {
25
60
  const id = encodeURIComponent(filename.slice(0, -3));
26
- const full = filename.match(FULL_PATTERN);
27
- if (full) {
28
- const [, dateStr, category, titlePart] = full;
29
- const date = dateStr.replace(/_/g, '-');
30
- return {
31
- id,
32
- filename,
33
- title: titleCase(titlePart),
34
- category,
35
- date,
36
- formattedDate: formatDate(date),
37
- };
61
+ const { full: FULL_PAT, dateOnly: DATE_ONLY_PAT, hasDate, hasCategory, catBeforeDate } = buildPatternsFromFormat(filenamePattern ?? 'YYYY_MM_DD_[Category]_title');
62
+ if (FULL_PAT) {
63
+ const full = filename.match(FULL_PAT);
64
+ if (full) {
65
+ if (hasDate && hasCategory) {
66
+ const dateStr = catBeforeDate ? full[2] : full[1];
67
+ const category = catBeforeDate ? full[1] : full[2];
68
+ const titlePart = full[3];
69
+ const date = dateStr.replace(/_/g, '-');
70
+ return { id, filename, title: titleCase(titlePart), category, date, formattedDate: formatDate(date) };
71
+ }
72
+ else if (hasCategory) {
73
+ const [, category, titlePart] = full;
74
+ return { id, filename, title: titleCase(titlePart), category, date: null, formattedDate: null };
75
+ }
76
+ }
38
77
  }
39
- const dateOnly = filename.match(DATE_ONLY_PATTERN);
40
- if (dateOnly) {
41
- const [, dateStr, titlePart] = dateOnly;
42
- const date = dateStr.replace(/_/g, '-');
43
- return {
44
- id,
45
- filename,
46
- title: titleCase(titlePart),
47
- category: 'General',
48
- date,
49
- formattedDate: formatDate(date),
50
- };
78
+ if (DATE_ONLY_PAT) {
79
+ const dateOnly = filename.match(DATE_ONLY_PAT);
80
+ if (dateOnly) {
81
+ const [, dateStr, titlePart] = dateOnly;
82
+ const date = dateStr.replace(/_/g, '-');
83
+ return { id, filename, title: titleCase(titlePart), category: 'General', date, formattedDate: formatDate(date) };
84
+ }
51
85
  }
52
86
  // Fallback — no date, no category
53
87
  const rawTitle = filename.slice(0, -3).replace(/[_-]+/g, ' ');
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/lib/parser.ts"],"names":[],"mappings":";;AAiCA,sCAyCC;AAjED,gDAAgD;AAChD,MAAM,YAAY,GAAG,8CAA8C,CAAC;AAEpE,mDAAmD;AACnD,MAAM,iBAAiB,GAAG,iCAAiC,CAAC;AAE5D,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE;QACnC,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,SAAS;QACd,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,GAAW;IAC5B,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SACzE,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,SAAgB,aAAa,CAAC,QAAgB;IAC5C,MAAM,EAAE,GAAG,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAErD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1C,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;QAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxC,OAAO;YACL,EAAE;YACF,QAAQ;YACR,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC;YAC3B,QAAQ;YACR,IAAI;YACJ,aAAa,EAAE,UAAU,CAAC,IAAI,CAAC;SAChC,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACnD,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC;QACxC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxC,OAAO;YACL,EAAE;YACF,QAAQ;YACR,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC;YAC3B,QAAQ,EAAE,SAAS;YACnB,IAAI;YACJ,aAAa,EAAE,UAAU,CAAC,IAAI,CAAC;SAChC,CAAC;IACJ,CAAC;IAED,kCAAkC;IAClC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC9D,OAAO;QACL,EAAE;QACF,QAAQ;QACR,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,IAAI;QACV,aAAa,EAAE,IAAI;KACpB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/lib/parser.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAIpD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CA6CrD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/routes/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAIpD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuDrD"}
@@ -34,6 +34,16 @@ function configRouter(docsPath) {
34
34
  safe[key] = patch[key];
35
35
  }
36
36
  }
37
+ // filenamePattern: must contain exactly one [Category]
38
+ if ('filenamePattern' in safe && typeof safe.filenamePattern === 'string') {
39
+ const matches = safe.filenamePattern.match(/\[Category\]/gi);
40
+ if (!matches || matches.length === 0) {
41
+ return res.status(400).json({ error: 'filenamePattern must contain [Category]' });
42
+ }
43
+ if (matches.length > 1) {
44
+ return res.status(400).json({ error: 'filenamePattern must contain [Category] exactly once' });
45
+ }
46
+ }
37
47
  // extraFiles: only absolute .md paths
38
48
  if ('extraFiles' in patch && Array.isArray(patch.extraFiles)) {
39
49
  safe.extraFiles = patch.extraFiles.filter((f) => typeof f === 'string' &&
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/routes/config.ts"],"names":[],"mappings":";;;;;AAIA,oCA6CC;AAjDD,qCAAoD;AACpD,gDAAwB;AACxB,0CAAyE;AAEzE,SAAgB,YAAY,CAAC,QAAgB;IAC3C,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,kBAAkB;IAClB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,kBAAkB;IAClB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QAC9C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,IAAgC,CAAC;YACnD,6DAA6D;YAC7D,MAAM,OAAO,GAA8B;gBACzC,OAAO;gBACP,iBAAiB;gBACjB,OAAO;aACR,CAAC;YACF,MAAM,IAAI,GAA6B,EAAE,CAAC;YAC1C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;oBAChB,IAAgC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;YACD,sCAAsC;YACtC,IAAI,YAAY,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,UAAU,GAAI,KAAK,CAAC,UAAwB,CAAC,MAAM,CACtD,CAAC,CAAC,EAAe,EAAE,CACjB,OAAO,CAAC,KAAK,QAAQ;oBACrB,cAAI,CAAC,UAAU,CAAC,CAAC,CAAC;oBAClB,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAClC,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,IAAA,oBAAW,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/routes/config.ts"],"names":[],"mappings":";;;;;AAIA,oCAuDC;AA3DD,qCAAoD;AACpD,gDAAwB;AACxB,0CAAyE;AAEzE,SAAgB,YAAY,CAAC,QAAgB;IAC3C,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,kBAAkB;IAClB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,kBAAkB;IAClB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QAC9C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,IAAgC,CAAC;YACnD,6DAA6D;YAC7D,MAAM,OAAO,GAA8B;gBACzC,OAAO;gBACP,iBAAiB;gBACjB,OAAO;aACR,CAAC;YACF,MAAM,IAAI,GAA6B,EAAE,CAAC;YAC1C,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;oBAChB,IAAgC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;YACD,uDAAuD;YACvD,IAAI,iBAAiB,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,QAAQ,EAAE,CAAC;gBAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;gBAC7D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACrC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC,CAAC;gBACpF,CAAC;gBACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sDAAsD,EAAE,CAAC,CAAC;gBACjG,CAAC;YACH,CAAC;YACD,sCAAsC;YACtC,IAAI,YAAY,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7D,IAAI,CAAC,UAAU,GAAI,KAAK,CAAC,UAAwB,CAAC,MAAM,CACtD,CAAC,CAAC,EAAe,EAAE,CACjB,OAAO,CAAC,KAAK,QAAQ;oBACrB,cAAI,CAAC,UAAU,CAAC,CAAC,CAAC;oBAClB,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAClC,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,IAAA,oBAAW,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC5C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -10,14 +10,14 @@ const path_1 = __importDefault(require("path"));
10
10
  const marked_1 = require("marked");
11
11
  const parser_1 = require("../lib/parser");
12
12
  const config_1 = require("../lib/config");
13
- function listDocs(docsPath, extraFiles = []) {
13
+ function listDocs(docsPath, extraFiles = [], filenamePattern) {
14
14
  // Extra files first, in config order, always General
15
15
  const extraDocs = [];
16
16
  for (const filePath of extraFiles) {
17
17
  if (!filePath.endsWith('.md') || !fs_1.default.existsSync(filePath))
18
18
  continue;
19
19
  const filename = path_1.default.basename(filePath);
20
- const meta = (0, parser_1.parseFilename)(filename);
20
+ const meta = (0, parser_1.parseFilename)(filename, filenamePattern);
21
21
  extraDocs.push({
22
22
  ...meta,
23
23
  id: encodeURIComponent(filePath.slice(0, -3)),
@@ -28,7 +28,7 @@ function listDocs(docsPath, extraFiles = []) {
28
28
  const regularDocs = fs_1.default
29
29
  .readdirSync(docsPath)
30
30
  .filter((f) => f.toLowerCase().endsWith('.md'))
31
- .map((filename) => (0, parser_1.parseFilename)(filename));
31
+ .map((filename) => (0, parser_1.parseFilename)(filename, filenamePattern));
32
32
  regularDocs.sort((a, b) => {
33
33
  if (a.date && b.date)
34
34
  return b.date.localeCompare(a.date);
@@ -59,8 +59,8 @@ function documentsRouter(docsPath) {
59
59
  // GET /api/documents — list all docs with metadata
60
60
  router.get('/', (_req, res) => {
61
61
  try {
62
- const { extraFiles = [] } = (0, config_1.readConfig)(docsPath);
63
- res.json(listDocs(docsPath, extraFiles));
62
+ const { extraFiles = [], filenamePattern } = (0, config_1.readConfig)(docsPath);
63
+ res.json(listDocs(docsPath, extraFiles, filenamePattern));
64
64
  }
65
65
  catch {
66
66
  res.status(500).json({ error: 'Failed to list documents' });
@@ -72,8 +72,8 @@ function documentsRouter(docsPath) {
72
72
  if (!query)
73
73
  return res.json([]);
74
74
  try {
75
- const { extraFiles = [] } = (0, config_1.readConfig)(docsPath);
76
- const docs = listDocs(docsPath, extraFiles);
75
+ const { extraFiles = [], filenamePattern } = (0, config_1.readConfig)(docsPath);
76
+ const docs = listDocs(docsPath, extraFiles, filenamePattern);
77
77
  const results = [];
78
78
  for (const doc of docs) {
79
79
  const filePath = resolveDocPath(docsPath, doc, extraFiles);
@@ -106,7 +106,7 @@ function documentsRouter(docsPath) {
106
106
  // GET /api/documents/:id — get a single document (content + rendered HTML)
107
107
  router.get('/:id', async (req, res) => {
108
108
  const id = decodeURIComponent(req.params.id);
109
- const { extraFiles = [] } = (0, config_1.readConfig)(docsPath);
109
+ const { extraFiles = [], filenamePattern } = (0, config_1.readConfig)(docsPath);
110
110
  // Extra file: id is an absolute path without .md
111
111
  if (path_1.default.isAbsolute(id)) {
112
112
  const filePath = id + '.md';
@@ -118,7 +118,7 @@ function documentsRouter(docsPath) {
118
118
  }
119
119
  try {
120
120
  const content = fs_1.default.readFileSync(filePath, 'utf-8');
121
- const meta = (0, parser_1.parseFilename)(path_1.default.basename(filePath));
121
+ const meta = (0, parser_1.parseFilename)(path_1.default.basename(filePath), filenamePattern);
122
122
  const html = marked_1.marked.parse(content);
123
123
  res.json({ ...meta, id: req.params.id, category: 'General', content, html });
124
124
  }
@@ -138,7 +138,7 @@ function documentsRouter(docsPath) {
138
138
  }
139
139
  try {
140
140
  const content = fs_1.default.readFileSync(filePath, 'utf-8');
141
- const metadata = (0, parser_1.parseFilename)(filename);
141
+ const metadata = (0, parser_1.parseFilename)(filename, filenamePattern);
142
142
  const html = marked_1.marked.parse(content);
143
143
  res.json({ ...metadata, content, html });
144
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../../src/routes/documents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"documents.js","sourceRoot":"","sources":["../../../src/routes/documents.ts"],"names":[],"mappings":";;;;;AAwDA,0CAqGC;AA7JD,qCAAoD;AACpD,4CAAoB;AACpB,gDAAwB;AACxB,mCAAgC;AAChC,0CAA2D;AAC3D,0CAA2C;AAE3C,SAAS,QAAQ,CAAC,QAAgB,EAAE,aAAuB,EAAE,EAAE,eAAwB;IACrF,qDAAqD;IACrD,MAAM,SAAS,GAAkB,EAAE,CAAC;IACpC,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QACpE,MAAM,QAAQ,GAAG,cAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,IAAA,sBAAa,EAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;QACtD,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,IAAI;YACP,EAAE,EAAE,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC7C,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;IACL,CAAC;IAED,uEAAuE;IACvE,MAAM,WAAW,GAAG,YAAE;SACnB,WAAW,CAAC,QAAQ,CAAC;SACrB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAC9C,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAA,sBAAa,EAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;IAE/D,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,SAAS,EAAE,GAAG,WAAW,CAAC,CAAC;AACxC,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,QAAgB;IACtD,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,cAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,cAAc,CACrB,QAAgB,EAChB,GAAgB,EAChB,UAAoB;IAEpB,MAAM,EAAE,GAAG,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,cAAI,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC;QAC5B,OAAO,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACzD,CAAC;IACD,OAAO,YAAY,CAAC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,eAAe,CAAC,QAAgB;IAC9C,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;IAExB,mDAAmD;IACnD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,eAAe,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;YAClE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;QACpD,MAAM,KAAK,GAAG,CAAE,GAAG,CAAC,KAAK,CAAC,CAAY,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACnE,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEhC,IAAI,CAAC;YACH,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,eAAe,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;YAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;YAC7D,MAAM,OAAO,GAA0C,EAAE,CAAC;YAE1D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;gBAC3D,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;oBAAE,SAAS;gBAEpD,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACnD,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACxD,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAExD,IAAI,OAAO,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;oBACvC,IAAI,OAAO,GAAG,EAAE,CAAC;oBACjB,IAAI,SAAS,EAAE,CAAC;wBACd,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;wBACjD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;wBACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;wBAC9D,OAAO;4BACL,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gCACtB,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;gCACrD,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACtC,CAAC;oBACD,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAC3E,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACvD,MAAM,EAAE,GAAG,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7C,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,eAAe,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;QAElE,iDAAiD;QACjD,IAAI,cAAI,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC;YAC5B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YAC/D,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBACnD,MAAM,IAAI,GAAG,IAAA,sBAAa,EAAC,cAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC;gBACrE,MAAM,IAAI,GAAG,eAAM,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;gBAC7C,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/E,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YAC7D,CAAC;YACD,OAAO;QACT,CAAC;QAED,8BAA8B;QAC9B,MAAM,QAAQ,GAAG,EAAE,GAAG,KAAK,CAAC;QAC5B,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAElD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,IAAA,sBAAa,EAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;YAC1D,MAAM,IAAI,GAAG,eAAM,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;YAC7C,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "living-documentation",
3
- "version": "1.1.0",
3
+ "version": "1.4.0",
4
4
  "description": "A CLI tool that serves a local Markdown documentation viewer",
5
5
  "main": "dist/src/server.js",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "build": "tsc && node scripts/copy-assets.js && chmod +x dist/bin/cli.js",
15
- "dev": "ts-node bin/cli.ts",
15
+ "dev": "nodemon --watch src --watch bin --ext ts,html --exec 'ts-node bin/cli.ts'",
16
16
  "start": "node dist/bin/cli.js",
17
17
  "prepublishOnly": "npm run build"
18
18
  },
@@ -32,6 +32,7 @@
32
32
  "devDependencies": {
33
33
  "@types/express": "^4.17.21",
34
34
  "@types/node": "^20.14.0",
35
+ "nodemon": "^3.1.14",
35
36
  "ts-node": "^10.9.2",
36
37
  "typescript": "^5.4.5"
37
38
  },