living-documentation 7.19.0 → 7.21.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.
@@ -18,7 +18,7 @@
18
18
  >
19
19
  <!-- ── Header ── -->
20
20
  <header
21
- class="flex items-center justify-between px-4 h-14 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm"
21
+ class="sticky top-0 z-40 flex items-center justify-between px-4 h-14 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm"
22
22
  >
23
23
  <div class="flex items-center gap-3">
24
24
  <span
@@ -48,48 +48,180 @@
48
48
  </header>
49
49
 
50
50
  <!-- ── Page ── -->
51
- <main class="max-w-6xl mx-auto px-6 py-10">
52
- <!-- ── Two-column layout ── -->
51
+ <main class="pb-10">
53
52
  <form id="config-form" novalidate>
54
- <!-- Title + Save on same row -->
55
- <div class="flex items-center justify-between mb-8">
56
- <div>
57
- <h2 class="text-xl font-bold text-gray-900 dark:text-gray-50" data-i18n="admin.config.title">
58
- Configuration
59
- </h2>
60
- <p class="mt-1 text-sm text-gray-500 dark:text-gray-400" data-i18n="admin.config.description">
61
- Settings are saved to .living-doc.json in your docs folder.
62
- </p>
63
- </div>
64
- <div class="flex items-center gap-4 shrink-0">
65
- <div id="save-msg" class="text-sm"></div>
66
- <button
67
- type="submit"
68
- 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"
69
- >
70
- <span data-i18n="admin.config.save_btn">Save changes</span>
71
- </button>
53
+ <!-- Sticky sub-header: Configuration title + Save button -->
54
+ <div
55
+ class="sticky top-14 z-30 border-b border-gray-200 dark:border-gray-800 bg-gray-50/90 dark:bg-gray-950/90 backdrop-blur"
56
+ >
57
+ <div
58
+ class="max-w-3xl mx-auto px-6 py-4 flex items-center justify-between gap-4"
59
+ >
60
+ <div class="min-w-0">
61
+ <h2
62
+ class="text-lg font-bold text-gray-900 dark:text-gray-50 truncate"
63
+ data-i18n="admin.config.title"
64
+ >
65
+ Configuration
66
+ </h2>
67
+ <p
68
+ class="mt-0.5 text-xs text-gray-500 dark:text-gray-400 truncate"
69
+ data-i18n="admin.config.description"
70
+ >
71
+ Settings are saved to .living-doc.json in your docs folder.
72
+ </p>
73
+ </div>
74
+ <div class="flex items-center gap-4 shrink-0">
75
+ <div id="save-msg" class="text-sm"></div>
76
+ <button
77
+ type="submit"
78
+ 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"
79
+ >
80
+ <span data-i18n="admin.config.save_btn">Save changes</span>
81
+ </button>
82
+ </div>
72
83
  </div>
73
84
  </div>
74
85
 
75
- <div class="grid grid-cols-2 gap-8 items-start">
76
- <!-- ── Left column ── -->
77
- <div class="space-y-6">
78
- <!-- Server Info -->
79
- <div
80
- class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5"
86
+ <div class="max-w-3xl mx-auto px-6 pt-8 space-y-8">
87
+
88
+ <!-- ── 🌐 General ── -->
89
+ <section
90
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
91
+ >
92
+ <header
93
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
81
94
  >
82
95
  <h3
83
- class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-4"
96
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
84
97
  >
85
- <span data-i18n="admin.server_info.title">Server Info</span>
98
+ <span class="text-xl" aria-hidden="true">🌐</span>
99
+ <span data-i18n="admin.section.general.title">General</span>
86
100
  </h3>
87
- <dl class="grid grid-cols-[1fr_auto] gap-x-6 gap-y-3 text-sm">
101
+ <p
102
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
103
+ data-i18n="admin.section.general.desc"
104
+ >
105
+ Site identity, theme, and interface language.
106
+ </p>
107
+ </header>
108
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
109
+ <div class="px-6 py-4 space-y-1.5">
110
+ <label
111
+ class="block text-sm font-medium text-gray-700 dark:text-gray-300"
112
+ for="field-title"
113
+ data-i18n="admin.appearance.site_title_label"
114
+ >Site Title</label
115
+ >
116
+ <input
117
+ id="field-title"
118
+ name="title"
119
+ type="text"
120
+ 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"
121
+ placeholder="Living Documentation"
122
+ />
123
+ <p
124
+ class="text-xs text-gray-400 dark:text-gray-500"
125
+ data-i18n="admin.appearance.site_title_hint"
126
+ >
127
+ Displayed in the browser tab and sidebar header.
128
+ </p>
129
+ </div>
130
+ <div class="px-6 py-4 space-y-1.5">
131
+ <label
132
+ class="block text-sm font-medium text-gray-700 dark:text-gray-300"
133
+ for="field-theme"
134
+ data-i18n="admin.appearance.theme_label"
135
+ >Default Theme</label
136
+ >
137
+ <select
138
+ id="field-theme"
139
+ name="theme"
140
+ 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"
141
+ >
142
+ <option
143
+ value="system"
144
+ data-i18n="admin.appearance.theme_system"
145
+ >
146
+ System (follow OS preference)
147
+ </option>
148
+ <option
149
+ value="light"
150
+ data-i18n="admin.appearance.theme_light"
151
+ >
152
+ Light
153
+ </option>
154
+ <option value="dark" data-i18n="admin.appearance.theme_dark">
155
+ Dark
156
+ </option>
157
+ </select>
158
+ <p
159
+ class="text-xs text-gray-400 dark:text-gray-500"
160
+ data-i18n="admin.appearance.theme_hint"
161
+ >
162
+ Users can always override with the toggle button.
163
+ </p>
164
+ </div>
165
+ <div class="px-6 py-4 space-y-1.5">
166
+ <label
167
+ class="block text-sm font-medium text-gray-700 dark:text-gray-300"
168
+ for="field-language"
169
+ data-i18n="admin.appearance.language_label"
170
+ >Language</label
171
+ >
172
+ <select
173
+ id="field-language"
174
+ name="language"
175
+ 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"
176
+ >
177
+ <option value="en" data-i18n="admin.appearance.language_en">
178
+ English
179
+ </option>
180
+ <option value="fr" data-i18n="admin.appearance.language_fr">
181
+ Français
182
+ </option>
183
+ </select>
184
+ <p
185
+ class="text-xs text-gray-400 dark:text-gray-500"
186
+ data-i18n="admin.appearance.language_hint"
187
+ >
188
+ Interface language for all pages.
189
+ </p>
190
+ </div>
191
+ </div>
192
+ </section>
193
+
194
+ <!-- ── 📝 Filename convention ── -->
195
+ <section
196
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
197
+ >
198
+ <header
199
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
200
+ >
201
+ <h3
202
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
203
+ >
204
+ <span class="text-xl" aria-hidden="true">📝</span>
205
+ <span data-i18n="admin.section.filename.title"
206
+ >Filename convention</span
207
+ >
208
+ </h3>
209
+ <p
210
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
211
+ data-i18n="admin.section.filename.desc"
212
+ >
213
+ How new documents are named, and how the viewer parses them.
214
+ </p>
215
+ </header>
216
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
217
+ <div class="px-6 py-4 grid grid-cols-[1fr_auto] gap-x-6 gap-y-2">
88
218
  <div>
89
219
  <dt
90
220
  class="text-xs text-gray-400 uppercase tracking-wide mb-0.5"
91
221
  >
92
- <span data-i18n="admin.server_info.docs_folder">Docs Folder</span>
222
+ <span data-i18n="admin.server_info.docs_folder"
223
+ >Docs Folder</span
224
+ >
93
225
  </dt>
94
226
  <dd
95
227
  id="info-folder"
@@ -111,170 +243,182 @@
111
243
 
112
244
  </dd>
113
245
  </div>
114
- <div class="col-span-2 space-y-1.5">
115
- <label
116
- class="block text-xs text-gray-400 uppercase tracking-wide mb-0.5"
117
- for="field-source-root"
118
- data-i18n="admin.server_info.source_root"
119
- >Source Root</label
246
+ </div>
247
+ <div class="px-6 py-4 space-y-1.5">
248
+ <label
249
+ class="block text-sm font-medium text-gray-700 dark:text-gray-300"
250
+ for="field-pattern"
251
+ data-i18n="admin.pattern.label"
252
+ >Filename Pattern</label
253
+ >
254
+ <input
255
+ id="field-pattern"
256
+ name="filenamePattern"
257
+ type="text"
258
+ 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"
259
+ placeholder="YYYY_MM_DD_HH_mm_[Category]_title"
260
+ data-i18n-placeholder="admin.pattern.placeholder"
261
+ />
262
+ <p class="text-xs text-gray-400 dark:text-gray-500">
263
+ <span data-i18n="admin.pattern.hint"
264
+ >Parsed for date, category, and title.</span
120
265
  >
121
- <input
122
- id="field-source-root"
123
- name="sourceRoot"
124
- type="text"
125
- class="w-full px-3 py-2 font-mono text-xs rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 break-all"
126
- placeholder="/absolute/path"
127
- />
128
- <p
129
- class="text-xs text-gray-400 dark:text-gray-500"
130
- data-i18n="admin.server_info.source_root_hint"
266
+ <span
267
+ id="pattern-desc-example"
268
+ class="font-mono bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-1 rounded"
269
+ >YYYY_MM_DD_HH_mm_[Category]_title.md</span
131
270
  >
132
- Absolute path used by MCP source tools. Defaults to the parent of Docs Folder when empty.
133
- </p>
134
- </div>
135
- </dl>
136
- </div>
137
-
138
- <!-- Extra Files -->
139
- <div
140
- class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5 space-y-4"
141
- >
142
- <div>
143
- <h3
144
- class="text-sm font-semibold text-gray-700 dark:text-gray-300"
145
- >
146
- <span data-i18n="admin.extra_files.title">General — Extra Files</span>
147
- </h3>
148
- <p class="mt-1 text-xs text-gray-500 dark:text-gray-400" data-i18n="admin.extra_files.description">
149
- Add Markdown files from outside the docs folder to the General section.
150
271
  </p>
151
272
  </div>
152
-
153
- <!-- File browser -->
154
- <div
155
- class="rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden"
156
- >
157
- <div
158
- 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"
273
+ <div id="pattern-preview" class="px-6 py-4">
274
+ <h4
275
+ class="text-xs font-semibold text-blue-700 dark:text-blue-400 mb-2 uppercase tracking-wide"
159
276
  >
160
- <button
161
- id="browse-up"
162
- onclick="browseUp()"
163
- class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
277
+ <span data-i18n="admin.pattern.preview_title"
278
+ >Pattern Preview</span
164
279
  >
165
- <span data-i18n="common.up">↑ Up</span>
166
- </button>
167
- <span
168
- id="browse-path"
169
- class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
170
- ></span>
171
- </div>
172
- <div
173
- id="browse-list"
174
- class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"
175
- ></div>
176
- </div>
177
-
178
- <!-- Added files -->
179
- <div>
280
+ </h4>
180
281
  <p
181
- class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2"
282
+ class="text-xs text-gray-500 dark:text-gray-400 mb-3"
283
+ data-i18n="admin.pattern.preview_desc"
182
284
  >
183
- Added files
184
- </p>
185
- <div id="extra-files-list" class="space-y-1"></div>
186
- <p id="extra-files-empty" class="text-xs text-gray-400 italic">
187
- <span data-i18n="admin.extra_files.empty">No extra files added yet.</span>
285
+ How your pattern parses example filenames:
188
286
  </p>
287
+ <div id="preview-rows" class="space-y-2 text-sm"></div>
189
288
  </div>
190
289
  </div>
191
- </div>
290
+ </section>
192
291
 
193
- <!-- ── Right column ── -->
194
- <div class="space-y-6">
195
- <!-- Appearance & Metadata -->
196
- <div
197
- 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"
292
+ <!-- ── 📂 Source & extra files ── -->
293
+ <section
294
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
295
+ >
296
+ <header
297
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
198
298
  >
199
- <div class="px-5 py-4">
200
- <h3
201
- class="text-sm font-semibold text-gray-700 dark:text-gray-300"
299
+ <h3
300
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
301
+ >
302
+ <span class="text-xl" aria-hidden="true">📂</span>
303
+ <span data-i18n="admin.section.source.title"
304
+ >Source &amp; extra files</span
202
305
  >
203
- Appearance & Metadata
204
- </h3>
205
- </div>
206
- <div class="px-5 py-4 space-y-1.5">
306
+ </h3>
307
+ <p
308
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
309
+ data-i18n="admin.section.source.desc"
310
+ >
311
+ Code source root (for MCP tools) and Markdown files pulled from
312
+ outside the docs folder.
313
+ </p>
314
+ </header>
315
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
316
+ <div class="px-6 py-4 space-y-1.5">
207
317
  <label
208
318
  class="block text-sm font-medium text-gray-700 dark:text-gray-300"
209
- for="field-title"
210
- data-i18n="admin.appearance.site_title_label">Site Title</label
319
+ for="field-source-root"
320
+ data-i18n="admin.server_info.source_root"
321
+ >Source Root</label
211
322
  >
212
323
  <input
213
- id="field-title"
214
- name="title"
324
+ id="field-source-root"
325
+ name="sourceRoot"
215
326
  type="text"
216
- 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"
217
- placeholder="Living Documentation"
327
+ class="w-full px-3 py-2 font-mono text-xs rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-300 placeholder:text-gray-400 dark:placeholder:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 break-all"
328
+ placeholder="/absolute/path"
218
329
  />
219
- <p class="text-xs text-gray-400 dark:text-gray-500" data-i18n="admin.appearance.site_title_hint">
220
- Displayed in the browser tab and sidebar header.
221
- </p>
222
- </div>
223
- <div class="px-5 py-4 space-y-1.5">
224
- <label
225
- class="block text-sm font-medium text-gray-700 dark:text-gray-300"
226
- for="field-theme"
227
- data-i18n="admin.appearance.theme_label">Default Theme</label
228
- >
229
- <select
230
- id="field-theme"
231
- name="theme"
232
- 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"
233
- >
234
- <option value="system" data-i18n="admin.appearance.theme_system">System (follow OS preference)</option>
235
- <option value="light" data-i18n="admin.appearance.theme_light">Light</option>
236
- <option value="dark" data-i18n="admin.appearance.theme_dark">Dark</option>
237
- </select>
238
- <p class="text-xs text-gray-400 dark:text-gray-500" data-i18n="admin.appearance.theme_hint">
239
- Users can always override with the toggle button.
240
- </p>
241
- </div>
242
- <div class="px-5 py-4 space-y-1.5">
243
- <label
244
- class="block text-sm font-medium text-gray-700 dark:text-gray-300"
245
- for="field-language"
246
- data-i18n="admin.appearance.language_label">Language</label>
247
- <select
248
- id="field-language"
249
- name="language"
250
- 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"
330
+ <p
331
+ class="text-xs text-gray-400 dark:text-gray-500"
332
+ data-i18n="admin.server_info.source_root_hint"
251
333
  >
252
- <option value="en" data-i18n="admin.appearance.language_en">English</option>
253
- <option value="fr" data-i18n="admin.appearance.language_fr">Français</option>
254
- </select>
255
- <p class="text-xs text-gray-400 dark:text-gray-500" data-i18n="admin.appearance.language_hint">
256
- Interface language for all pages.
334
+ Absolute path used by MCP source tools. Defaults to the parent
335
+ of Docs Folder when empty.
257
336
  </p>
258
337
  </div>
259
- <div class="px-5 py-4">
260
- <label class="flex items-center gap-3 cursor-pointer">
261
- <input
262
- id="field-debug"
263
- name="showDiagramDebug"
264
- type="checkbox"
265
- class="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500"
266
- />
267
- <span
338
+ <div class="px-6 py-4 space-y-4">
339
+ <div>
340
+ <p
268
341
  class="text-sm font-medium text-gray-700 dark:text-gray-300"
269
- data-i18n="admin.appearance.debug_label">Show debug button in diagram editor</span
342
+ data-i18n="admin.extra_files.title"
270
343
  >
271
- </label>
272
- <p class="mt-1 text-xs text-gray-400 dark:text-gray-500 ml-7">
273
- Displays a Debug button that overlays raw position and size
274
- info on each node — useful for diagnosing snap-to-grid issues.
275
- </p>
344
+ General — Extra Files
345
+ </p>
346
+ <p
347
+ class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
348
+ data-i18n="admin.extra_files.description"
349
+ >
350
+ Add Markdown files from outside the docs folder to the
351
+ General section.
352
+ </p>
353
+ </div>
354
+ <div
355
+ class="rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden"
356
+ >
357
+ <div
358
+ 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"
359
+ >
360
+ <button
361
+ id="browse-up"
362
+ onclick="browseUp()"
363
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
364
+ >
365
+ <span data-i18n="common.up">↑ Up</span>
366
+ </button>
367
+ <span
368
+ id="browse-path"
369
+ class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
370
+ ></span>
371
+ </div>
372
+ <div
373
+ id="browse-list"
374
+ class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"
375
+ ></div>
376
+ </div>
377
+ <div>
378
+ <p
379
+ class="text-xs font-medium text-gray-500 dark:text-gray-400 mb-2"
380
+ data-i18n="admin.section.added_files_label"
381
+ >
382
+ Added files
383
+ </p>
384
+ <div id="extra-files-list" class="space-y-1"></div>
385
+ <p
386
+ id="extra-files-empty"
387
+ class="text-xs text-gray-400 italic"
388
+ >
389
+ <span data-i18n="admin.extra_files.empty"
390
+ >No extra files added yet.</span
391
+ >
392
+ </p>
393
+ </div>
276
394
  </div>
277
- <div class="px-5 py-4">
395
+ </div>
396
+ </section>
397
+
398
+ <!-- ── 🌳 Sidebar behaviour ── -->
399
+ <section
400
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
401
+ >
402
+ <header
403
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
404
+ >
405
+ <h3
406
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
407
+ >
408
+ <span class="text-xl" aria-hidden="true">🗂️</span>
409
+ <span data-i18n="admin.section.sidebar.title"
410
+ >Sidebar behaviour</span
411
+ >
412
+ </h3>
413
+ <p
414
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
415
+ data-i18n="admin.section.sidebar.desc"
416
+ >
417
+ How folders and categories expand in the left drawer.
418
+ </p>
419
+ </header>
420
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
421
+ <div class="px-6 py-4">
278
422
  <label class="flex items-center gap-3 cursor-pointer">
279
423
  <input
280
424
  id="field-exclusive-folder"
@@ -284,7 +428,8 @@
284
428
  />
285
429
  <span
286
430
  class="text-sm font-medium text-gray-700 dark:text-gray-300"
287
- data-i18n="admin.appearance.exclusive_folder_label">Exclusive folder expansion in sidebar</span
431
+ data-i18n="admin.appearance.exclusive_folder_label"
432
+ >Exclusive folder expansion in sidebar</span
288
433
  >
289
434
  </label>
290
435
  <p
@@ -296,7 +441,7 @@
296
441
  children).
297
442
  </p>
298
443
  </div>
299
- <div class="px-5 py-4">
444
+ <div class="px-6 py-4">
300
445
  <label class="flex items-center gap-3 cursor-pointer">
301
446
  <input
302
447
  id="field-exclusive-category"
@@ -306,7 +451,8 @@
306
451
  />
307
452
  <span
308
453
  class="text-sm font-medium text-gray-700 dark:text-gray-300"
309
- data-i18n="admin.appearance.exclusive_category_label">Exclusive category expansion in sidebar</span
454
+ data-i18n="admin.appearance.exclusive_category_label"
455
+ >Exclusive category expansion in sidebar</span
310
456
  >
311
457
  </label>
312
458
  <p
@@ -317,7 +463,33 @@
317
463
  other open category under the same parent folder.
318
464
  </p>
319
465
  </div>
320
- <div class="px-5 py-4 space-y-1.5">
466
+ </div>
467
+ </section>
468
+
469
+ <!-- ── ✍️ Markdown rendering ── -->
470
+ <section
471
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
472
+ >
473
+ <header
474
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
475
+ >
476
+ <h3
477
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
478
+ >
479
+ <span class="text-xl" aria-hidden="true">✍️</span>
480
+ <span data-i18n="admin.section.markdown.title"
481
+ >Markdown rendering</span
482
+ >
483
+ </h3>
484
+ <p
485
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
486
+ data-i18n="admin.section.markdown.desc"
487
+ >
488
+ How the viewer renders newlines and long code blocks.
489
+ </p>
490
+ </header>
491
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
492
+ <div class="px-6 py-4 space-y-1.5">
321
493
  <label
322
494
  class="block text-sm font-medium text-gray-700 dark:text-gray-300"
323
495
  for="field-code-max-height"
@@ -338,10 +510,11 @@
338
510
  class="text-xs text-gray-400 dark:text-gray-500"
339
511
  data-i18n="admin.appearance.code_max_height_hint"
340
512
  >
341
- Longer code blocks get a collapse/expand button. Set to 0 to disable.
513
+ Longer code blocks get a collapse/expand button. Set to 0 to
514
+ disable.
342
515
  </p>
343
516
  </div>
344
- <div class="px-5 py-4">
517
+ <div class="px-6 py-4">
345
518
  <label class="flex items-center gap-3 cursor-pointer">
346
519
  <input
347
520
  id="field-soft-breaks"
@@ -359,164 +532,213 @@
359
532
  class="mt-1 text-xs text-gray-400 dark:text-gray-500 ml-7"
360
533
  data-i18n-html="admin.appearance.soft_breaks_hint"
361
534
  >
362
- <strong>Unchecked (CommonMark, default):</strong> a single newline in the source is rendered as a space — two adjacent lines merge into one paragraph. To force a line break without leaving a blank line, end the previous line with <code>two spaces</code> then newline, or use <code>&lt;br/&gt;</code>.<br/>
363
- <strong>Checked (GitHub-flavoured):</strong> every single newline becomes a real line break in the rendered output. Convenient for chat-style writing, but you can no longer hard-wrap long paragraphs in the source file without visible breaks.
364
- </p>
365
- </div>
366
- <div class="px-5 py-4 space-y-1.5">
367
- <label
368
- class="block text-sm font-medium text-gray-700 dark:text-gray-300"
369
- for="field-pattern"
370
- data-i18n="admin.pattern.label">Filename Pattern</label
371
- >
372
- <input
373
- id="field-pattern"
374
- name="filenamePattern"
375
- type="text"
376
- 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"
377
- placeholder="YYYY_MM_DD_HH_mm_[Category]_title"
378
- />
379
- <p class="text-xs text-gray-400 dark:text-gray-500">
380
- Parsed for date, category, and title.
381
- <span
382
- id="pattern-desc-example"
383
- class="font-mono bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-1 rounded"
384
- >YYYY_MM_DD_HH_mm_[Category]_title.md</span
385
- >
535
+ <strong>Unchecked (CommonMark, default):</strong> a single
536
+ newline in the source is rendered as a space two adjacent
537
+ lines merge into one paragraph. To force a line break without
538
+ leaving a blank line, end the previous line with
539
+ <code>two spaces</code> then newline, or use
540
+ <code>&lt;br/&gt;</code>.<br />
541
+ <strong>Checked (GitHub-flavoured):</strong> every single
542
+ newline becomes a real line break in the rendered output.
543
+ Convenient for chat-style writing, but you can no longer
544
+ hard-wrap long paragraphs in the source file without visible
545
+ breaks.
386
546
  </p>
387
547
  </div>
388
548
  </div>
549
+ </section>
389
550
 
390
- <!-- Pattern Preview -->
391
- <div
392
- id="pattern-preview"
393
- class="rounded-xl border border-blue-100 dark:border-blue-900 bg-blue-50 dark:bg-blue-950/30 p-5"
551
+ <!-- ── 📎 File attachments ── -->
552
+ <section
553
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
554
+ >
555
+ <header
556
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
394
557
  >
395
558
  <h3
396
- class="text-sm font-semibold text-blue-700 dark:text-blue-400 mb-3"
559
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
397
560
  >
398
- Pattern Preview
561
+ <span class="text-xl" aria-hidden="true">📎</span>
562
+ <span data-i18n="admin.section.files.title"
563
+ >File attachments</span
564
+ >
399
565
  </h3>
400
- <p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
401
- How your pattern parses example filenames:
566
+ <p
567
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
568
+ data-i18n-html="admin.files.description"
569
+ >
570
+ Extensions that are rejected by the
571
+ <code>/api/files/upload</code> endpoint. One extension per line
572
+ (or comma/space separated), without the leading dot.
573
+ </p>
574
+ </header>
575
+ <div class="px-6 py-4 space-y-1.5">
576
+ <label
577
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
578
+ for="field-blocked-extensions"
579
+ data-i18n="admin.files.blocked_label"
580
+ >Blocked extensions</label
581
+ >
582
+ <textarea
583
+ id="field-blocked-extensions"
584
+ name="blockedFileExtensions"
585
+ rows="3"
586
+ class="w-full px-3 py-2 font-mono text-xs 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"
587
+ placeholder="exe sh bat cmd com scr ps1 msi"
588
+ ></textarea>
589
+ <p
590
+ class="text-xs text-gray-400 dark:text-gray-500"
591
+ data-i18n="admin.files.hint"
592
+ >
593
+ Defaults to common executable formats. Max upload size is 19 MB
594
+ per file.
402
595
  </p>
403
- <div id="preview-rows" class="space-y-2 text-sm"></div>
404
596
  </div>
405
- </div>
406
- </div>
597
+ </section>
407
598
 
408
- <!-- ── File Attachments (full width) ── -->
409
- <div
410
- class="mt-8 rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 p-5 space-y-3"
411
- >
412
- <div>
413
- <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">
414
- <span data-i18n="admin.files.title">File Attachments</span>
415
- </h3>
416
- <p class="mt-1 text-xs text-gray-500 dark:text-gray-400" data-i18n="admin.files.description">
417
- Extensions that are rejected by the <code>/api/files/upload</code> endpoint. One extension per line (or comma/space separated), without the leading dot.
418
- </p>
419
- </div>
420
- <label
421
- class="block text-xs font-medium text-gray-500 dark:text-gray-400"
422
- for="field-blocked-extensions"
423
- data-i18n="admin.files.blocked_label"
424
- >Blocked extensions</label>
425
- <textarea
426
- id="field-blocked-extensions"
427
- name="blockedFileExtensions"
428
- rows="3"
429
- class="w-full px-3 py-2 font-mono text-xs 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"
430
- placeholder="exe sh bat cmd com scr ps1 msi"
431
- ></textarea>
432
- <p class="text-xs text-gray-400 dark:text-gray-500" data-i18n="admin.files.hint">
433
- Defaults to common executable formats. Max upload size is 19 MB per file.
434
- </p>
435
- </div>
436
-
437
- <!-- ── Diagram Palettes (full width) ── -->
438
- <div
439
- class="mt-8 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"
440
- >
441
- <div class="px-5 py-4">
442
- <h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300">
443
- Diagram Color Palettes
444
- </h3>
445
- <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
446
- Customize the colors available in the diagram editor. Changes take
447
- effect immediately.
448
- </p>
449
- </div>
450
- <div
451
- class="grid grid-cols-2 divide-x divide-gray-100 dark:divide-gray-800"
599
+ <!-- ── 🎨 Diagram palettes ── -->
600
+ <section
601
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
452
602
  >
453
- <!-- Node palette -->
454
- <div class="px-5 py-4 space-y-3">
455
- <div class="flex items-center justify-between">
456
- <h4
457
- class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"
603
+ <header
604
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
605
+ >
606
+ <h3
607
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
608
+ >
609
+ <span class="text-xl" aria-hidden="true">🎨</span>
610
+ <span data-i18n="admin.section.diagram.title"
611
+ >Diagram palettes</span
458
612
  >
459
- Shape colors
460
- </h4>
461
- <button
462
- type="button"
463
- onclick="resetNodePalette()"
464
- class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
613
+ </h3>
614
+ <p
615
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
616
+ data-i18n="admin.section.diagram.desc"
617
+ >
618
+ Customize the colors available in the diagram editor.
619
+ </p>
620
+ </header>
621
+ <div class="divide-y divide-gray-100 dark:divide-gray-800">
622
+ <div class="px-6 py-4 space-y-3">
623
+ <div class="flex items-center justify-between">
624
+ <h4
625
+ class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"
626
+ data-i18n="admin.palette.shapes_title"
627
+ >
628
+ Shape colors
629
+ </h4>
630
+ <button
631
+ type="button"
632
+ onclick="resetNodePalette()"
633
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
634
+ data-i18n="common.reset"
635
+ >
636
+ Reset
637
+ </button>
638
+ </div>
639
+ <p
640
+ class="text-xs text-gray-400 dark:text-gray-500"
641
+ data-i18n="admin.palette.shapes_hint"
465
642
  >
466
- Reset
467
- </button>
643
+ Click a color to show or hide it in the node panel. Dimmed =
644
+ hidden.
645
+ </p>
646
+ <div id="nodeSwatches" class="flex flex-wrap gap-2"></div>
647
+ </div>
648
+ <div class="px-6 py-4 space-y-3">
649
+ <div class="flex items-center justify-between">
650
+ <h4
651
+ class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"
652
+ data-i18n="admin.palette.arrows_title"
653
+ >
654
+ Arrow colors
655
+ </h4>
656
+ <button
657
+ type="button"
658
+ onclick="resetEdgePalette()"
659
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
660
+ data-i18n="common.reset"
661
+ >
662
+ Reset
663
+ </button>
664
+ </div>
665
+ <p
666
+ class="text-xs text-gray-400 dark:text-gray-500"
667
+ data-i18n="admin.palette.arrows_hint"
668
+ >
669
+ Add or remove colors available for arrows. Click ✕ to remove.
670
+ </p>
671
+ <div
672
+ id="edgeSwatches"
673
+ class="flex flex-wrap gap-3 items-end"
674
+ ></div>
675
+ <div class="flex items-center gap-2 pt-1">
676
+ <input
677
+ type="color"
678
+ id="newEdgeColor"
679
+ value="#3b82f6"
680
+ class="w-8 h-8 rounded cursor-pointer border border-gray-300 dark:border-gray-600 bg-transparent"
681
+ />
682
+ <button
683
+ type="button"
684
+ onclick="addEdgeColor()"
685
+ class="px-3 py-1.5 text-xs rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors"
686
+ data-i18n="admin.palette.add_color_btn"
687
+ >
688
+ Add color
689
+ </button>
690
+ </div>
468
691
  </div>
469
- <p class="text-xs text-gray-400 dark:text-gray-500">
470
- Click a color to show or hide it in the node panel. Dimmed =
471
- hidden.
472
- </p>
473
- <div id="nodeSwatches" class="flex flex-wrap gap-2"></div>
474
692
  </div>
693
+ </section>
475
694
 
476
- <!-- Edge palette -->
477
- <div class="px-5 py-4 space-y-3">
478
- <div class="flex items-center justify-between">
479
- <h4
480
- class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide"
481
- >
482
- Arrow colors
483
- </h4>
484
- <button
485
- type="button"
486
- onclick="resetEdgePalette()"
487
- class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
695
+ <!-- ── 🛠️ Developer ── -->
696
+ <section
697
+ class="rounded-xl border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm overflow-hidden"
698
+ >
699
+ <header
700
+ class="px-6 py-4 border-b border-gray-100 dark:border-gray-800"
701
+ >
702
+ <h3
703
+ class="text-base font-semibold text-gray-800 dark:text-gray-100 flex items-center gap-2"
704
+ >
705
+ <span class="text-xl" aria-hidden="true">🛠️</span>
706
+ <span data-i18n="admin.section.developer.title"
707
+ >Developer</span
488
708
  >
489
- Reset
490
- </button>
491
- </div>
492
- <p class="text-xs text-gray-400 dark:text-gray-500">
493
- Add or remove colors available for arrows. Click ✕ to remove.
709
+ </h3>
710
+ <p
711
+ class="mt-1 text-xs text-gray-500 dark:text-gray-400 ml-8"
712
+ data-i18n="admin.section.developer.desc"
713
+ >
714
+ Advanced options for power users.
494
715
  </p>
495
- <div
496
- id="edgeSwatches"
497
- class="flex flex-wrap gap-3 items-end"
498
- ></div>
499
- <div class="flex items-center gap-2 pt-1">
716
+ </header>
717
+ <div class="px-6 py-4">
718
+ <label class="flex items-center gap-3 cursor-pointer">
500
719
  <input
501
- type="color"
502
- id="newEdgeColor"
503
- value="#3b82f6"
504
- class="w-8 h-8 rounded cursor-pointer border border-gray-300 dark:border-gray-600 bg-transparent"
720
+ id="field-debug"
721
+ name="showDiagramDebug"
722
+ type="checkbox"
723
+ class="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500"
505
724
  />
506
- <button
507
- type="button"
508
- onclick="addEdgeColor()"
509
- class="px-3 py-1.5 text-xs rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors"
725
+ <span
726
+ class="text-sm font-medium text-gray-700 dark:text-gray-300"
727
+ data-i18n="admin.appearance.debug_label"
728
+ >Show debug button in diagram editor</span
510
729
  >
511
- Add color
512
- </button>
513
- </div>
730
+ </label>
731
+ <p class="mt-1 text-xs text-gray-400 dark:text-gray-500 ml-7">
732
+ Displays a Debug button that overlays raw position and size info
733
+ on each node — useful for diagnosing snap-to-grid issues.
734
+ </p>
514
735
  </div>
515
- </div>
736
+ </section>
516
737
  </div>
517
738
  </form>
518
739
 
519
740
  <!-- ── AGPL-3.0 License ── -->
741
+ <div class="max-w-3xl mx-auto px-6">
520
742
  <div class="mt-12 rounded-2xl overflow-hidden border border-blue-100 dark:border-blue-900/40 shadow-sm">
521
743
  <!-- Gradient banner -->
522
744
  <div class="bg-gradient-to-r from-blue-600 via-blue-500 to-indigo-500 px-6 py-5 flex items-center gap-4">
@@ -565,6 +787,7 @@
565
787
  </div>
566
788
  </div>
567
789
  </div>
790
+ </div>
568
791
  </main>
569
792
 
570
793
  <script>
@@ -603,6 +826,7 @@
603
826
  const cfg = await fetch("/api/config").then((r) => r.json());
604
827
  await window.initI18n(cfg.language || 'en');
605
828
  window.applyI18n();
829
+ window._ldCurrentLanguage = cfg.language || 'en';
606
830
  document.getElementById("info-folder").textContent =
607
831
  cfg.docsFolder || "—";
608
832
  document.getElementById("info-port").textContent = cfg.port || "—";
@@ -697,6 +921,12 @@
697
921
  body: JSON.stringify(payload),
698
922
  });
699
923
  if (!res.ok) throw new Error(await res.text());
924
+ if (payload.language && payload.language !== window._ldCurrentLanguage) {
925
+ await window.initI18n(payload.language);
926
+ window.applyI18n();
927
+ window._ldCurrentLanguage = payload.language;
928
+ updatePreview();
929
+ }
700
930
  showMsg(window.t('admin.msg.saved'), "ok");
701
931
  } catch (err) {
702
932
  showMsg(window.t('admin.msg.save_failed') + err.message, "error");
@@ -819,6 +1049,12 @@
819
1049
  (patternVal || "YYYY_MM_DD_HH_mm_[Category]_title") + ".md";
820
1050
  const patterns = buildPatternsFromFormat(patternVal);
821
1051
  const rows = document.getElementById("preview-rows");
1052
+ const tr = (k, fb) =>
1053
+ (typeof window.t === "function" && window.t(k)) || fb;
1054
+ const labelDate = tr("admin.pattern.col_date", "Date");
1055
+ const labelCat = tr("admin.pattern.col_category", "Category");
1056
+ const labelTitle = tr("admin.pattern.col_title", "Title");
1057
+ const labelNone = tr("admin.pattern.col_none", "none");
822
1058
  rows.innerHTML = EXAMPLES.map((f) => {
823
1059
  const p = parsePreview(f, patterns);
824
1060
  return `
@@ -826,15 +1062,15 @@
826
1062
  <p class="font-mono text-xs text-gray-500 dark:text-gray-400 mb-2 break-all">${esc(f)}</p>
827
1063
  <div class="grid grid-cols-3 gap-2 text-xs">
828
1064
  <div>
829
- <span class="text-gray-400 block mb-0.5">Date</span>
830
- <span class="${p.date ? "text-green-600 dark:text-green-400" : "text-gray-400"}">${p.date || "none"}</span>
1065
+ <span class="text-gray-400 block mb-0.5">${labelDate}</span>
1066
+ <span class="${p.date ? "text-green-600 dark:text-green-400" : "text-gray-400"}">${p.date || labelNone}</span>
831
1067
  </div>
832
1068
  <div>
833
- <span class="text-gray-400 block mb-0.5">Category</span>
1069
+ <span class="text-gray-400 block mb-0.5">${labelCat}</span>
834
1070
  <span class="text-blue-600 dark:text-blue-400">${esc(p.category)}</span>
835
1071
  </div>
836
1072
  <div>
837
- <span class="text-gray-400 block mb-0.5">Title</span>
1073
+ <span class="text-gray-400 block mb-0.5">${labelTitle}</span>
838
1074
  <span class="text-gray-700 dark:text-gray-300 truncate block">${esc(p.title)}</span>
839
1075
  </div>
840
1076
  </div>