living-documentation 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +329 -0
  3. package/dist/bin/cli.d.ts +3 -0
  4. package/dist/bin/cli.d.ts.map +1 -0
  5. package/dist/bin/cli.js +62 -0
  6. package/dist/bin/cli.js.map +1 -0
  7. package/dist/src/frontend/admin.html +1073 -0
  8. package/dist/src/frontend/annotations.js +546 -0
  9. package/dist/src/frontend/boot.js +90 -0
  10. package/dist/src/frontend/config.js +19 -0
  11. package/dist/src/frontend/dark-mode.js +20 -0
  12. package/dist/src/frontend/diagram/alignment.js +161 -0
  13. package/dist/src/frontend/diagram/clipboard.js +172 -0
  14. package/dist/src/frontend/diagram/constants.js +109 -0
  15. package/dist/src/frontend/diagram/debug.js +43 -0
  16. package/dist/src/frontend/diagram/edge-panel.js +260 -0
  17. package/dist/src/frontend/diagram/edge-rendering.js +12 -0
  18. package/dist/src/frontend/diagram/grid.js +78 -0
  19. package/dist/src/frontend/diagram/groups.js +102 -0
  20. package/dist/src/frontend/diagram/history.js +153 -0
  21. package/dist/src/frontend/diagram/image-name-modal.js +48 -0
  22. package/dist/src/frontend/diagram/image-upload.js +36 -0
  23. package/dist/src/frontend/diagram/label-editor.js +115 -0
  24. package/dist/src/frontend/diagram/link-panel.js +144 -0
  25. package/dist/src/frontend/diagram/main.js +299 -0
  26. package/dist/src/frontend/diagram/network.js +1473 -0
  27. package/dist/src/frontend/diagram/node-panel.js +267 -0
  28. package/dist/src/frontend/diagram/node-rendering.js +773 -0
  29. package/dist/src/frontend/diagram/persistence.js +161 -0
  30. package/dist/src/frontend/diagram/ports.js +386 -0
  31. package/dist/src/frontend/diagram/selection-overlay.js +336 -0
  32. package/dist/src/frontend/diagram/state.js +39 -0
  33. package/dist/src/frontend/diagram/t.js +3 -0
  34. package/dist/src/frontend/diagram/toast.js +21 -0
  35. package/dist/src/frontend/diagram/unlock-hold.js +182 -0
  36. package/dist/src/frontend/diagram/zoom.js +20 -0
  37. package/dist/src/frontend/diagram-link-modal.js +137 -0
  38. package/dist/src/frontend/diagram.html +1279 -0
  39. package/dist/src/frontend/documents.js +373 -0
  40. package/dist/src/frontend/export.js +338 -0
  41. package/dist/src/frontend/i18n/en.json +406 -0
  42. package/dist/src/frontend/i18n/fr.json +406 -0
  43. package/dist/src/frontend/i18n.js +32 -0
  44. package/dist/src/frontend/image-paste.js +101 -0
  45. package/dist/src/frontend/index.html +2314 -0
  46. package/dist/src/frontend/misc.js +25 -0
  47. package/dist/src/frontend/new-doc-modal.js +260 -0
  48. package/dist/src/frontend/new-folder-modal.js +174 -0
  49. package/dist/src/frontend/search.js +157 -0
  50. package/dist/src/frontend/sidebar-helpers.js +58 -0
  51. package/dist/src/frontend/sidebar.js +182 -0
  52. package/dist/src/frontend/snippet-detect.js +25 -0
  53. package/dist/src/frontend/snippet-table.js +85 -0
  54. package/dist/src/frontend/snippet-tree.js +94 -0
  55. package/dist/src/frontend/snippets.js +534 -0
  56. package/dist/src/frontend/state.js +28 -0
  57. package/dist/src/frontend/utils.js +21 -0
  58. package/dist/src/frontend/vendor/wordcloud2.js +1187 -0
  59. package/dist/src/frontend/wordcloud.js +693 -0
  60. package/dist/src/lib/config.d.ts +17 -0
  61. package/dist/src/lib/config.d.ts.map +1 -0
  62. package/dist/src/lib/config.js +79 -0
  63. package/dist/src/lib/config.js.map +1 -0
  64. package/dist/src/lib/parser.d.ts +11 -0
  65. package/dist/src/lib/parser.d.ts.map +1 -0
  66. package/dist/src/lib/parser.js +111 -0
  67. package/dist/src/lib/parser.js.map +1 -0
  68. package/dist/src/mcp/server.d.ts +3 -0
  69. package/dist/src/mcp/server.d.ts.map +1 -0
  70. package/dist/src/mcp/server.js +986 -0
  71. package/dist/src/mcp/server.js.map +1 -0
  72. package/dist/src/mcp/tools/diagrams.d.ts +44 -0
  73. package/dist/src/mcp/tools/diagrams.d.ts.map +1 -0
  74. package/dist/src/mcp/tools/diagrams.js +245 -0
  75. package/dist/src/mcp/tools/diagrams.js.map +1 -0
  76. package/dist/src/mcp/tools/documents.d.ts +26 -0
  77. package/dist/src/mcp/tools/documents.d.ts.map +1 -0
  78. package/dist/src/mcp/tools/documents.js +127 -0
  79. package/dist/src/mcp/tools/documents.js.map +1 -0
  80. package/dist/src/mcp/tools/source.d.ts +29 -0
  81. package/dist/src/mcp/tools/source.d.ts.map +1 -0
  82. package/dist/src/mcp/tools/source.js +200 -0
  83. package/dist/src/mcp/tools/source.js.map +1 -0
  84. package/dist/src/routes/annotations.d.ts +3 -0
  85. package/dist/src/routes/annotations.d.ts.map +1 -0
  86. package/dist/src/routes/annotations.js +83 -0
  87. package/dist/src/routes/annotations.js.map +1 -0
  88. package/dist/src/routes/browse.d.ts +3 -0
  89. package/dist/src/routes/browse.d.ts.map +1 -0
  90. package/dist/src/routes/browse.js +75 -0
  91. package/dist/src/routes/browse.js.map +1 -0
  92. package/dist/src/routes/config.d.ts +3 -0
  93. package/dist/src/routes/config.d.ts.map +1 -0
  94. package/dist/src/routes/config.js +97 -0
  95. package/dist/src/routes/config.js.map +1 -0
  96. package/dist/src/routes/diagrams.d.ts +3 -0
  97. package/dist/src/routes/diagrams.d.ts.map +1 -0
  98. package/dist/src/routes/diagrams.js +69 -0
  99. package/dist/src/routes/diagrams.js.map +1 -0
  100. package/dist/src/routes/documents.d.ts +8 -0
  101. package/dist/src/routes/documents.d.ts.map +1 -0
  102. package/dist/src/routes/documents.js +332 -0
  103. package/dist/src/routes/documents.js.map +1 -0
  104. package/dist/src/routes/export.d.ts +3 -0
  105. package/dist/src/routes/export.d.ts.map +1 -0
  106. package/dist/src/routes/export.js +277 -0
  107. package/dist/src/routes/export.js.map +1 -0
  108. package/dist/src/routes/images.d.ts +3 -0
  109. package/dist/src/routes/images.d.ts.map +1 -0
  110. package/dist/src/routes/images.js +49 -0
  111. package/dist/src/routes/images.js.map +1 -0
  112. package/dist/src/routes/wordcloud.d.ts +3 -0
  113. package/dist/src/routes/wordcloud.d.ts.map +1 -0
  114. package/dist/src/routes/wordcloud.js +95 -0
  115. package/dist/src/routes/wordcloud.js.map +1 -0
  116. package/dist/src/server.d.ts +7 -0
  117. package/dist/src/server.d.ts.map +1 -0
  118. package/dist/src/server.js +76 -0
  119. package/dist/src/server.js.map +1 -0
  120. package/dist/starting-doc/.annotations.json +3 -0
  121. package/dist/starting-doc/.diagrams.json +1884 -0
  122. package/dist/starting-doc/.living-doc.json +39 -0
  123. package/dist/starting-doc/1_tutorial/2026_04_11_13_25_[General]_crer_vos_dossiers.md +16 -0
  124. package/dist/starting-doc/1_tutorial/2026_04_11_18_58_[General]_creer_un_document_dans_un_dossier.md +9 -0
  125. package/dist/starting-doc/1_tutorial/2026_04_12_09_00_[General]_editer_et_sauvegarder.md +39 -0
  126. package/dist/starting-doc/1_tutorial/2026_04_12_10_00_[General]_utiliser_les_snippets.md +71 -0
  127. package/dist/starting-doc/2026_04_08_20_52_[General]_welcome.md +17 -0
  128. package/dist/starting-doc/2026_04_11_12_55_[General]_premiers_pas.md +271 -0
  129. package/dist/starting-doc/2_guide/2026_04_08_00_04_[DOCUMENT]_utilisation_des_images_plein_ecran_lien_clickable.md +40 -0
  130. package/dist/starting-doc/2_guide/2026_04_08_23_38_[Configuration]_demarrage_de_living_documentation.md +32 -0
  131. package/dist/starting-doc/2_guide/2026_04_09_09_00_[NAVIGATION]_recherche_plein_texte.md +65 -0
  132. package/dist/starting-doc/2_guide/2026_04_09_10_00_[EXPORT]_exporter_en_pdf.md +43 -0
  133. package/dist/starting-doc/2_guide/2026_04_09_11_00_[Configuration]_configurer_le_panneau_admin.md +55 -0
  134. package/dist/starting-doc/2_guide/2026_04_09_12_00_[Configuration]_extra_files.md +68 -0
  135. package/dist/starting-doc/2_guide/2026_04_09_13_00_[WORDCLOUD]_word_cloud.md +54 -0
  136. package/dist/starting-doc/2_guide/2026_04_09_14_00_[DIAGRAM]_creer_et_lier_un_diagramme.md +77 -0
  137. package/dist/starting-doc/3_concept/2026_04_08_20_58_[DOCUMENTING]_ADRS.md +20 -0
  138. package/dist/starting-doc/3_concept/2026_04_08_22_15_[DOCUMENTING]_living_documentation.md +17 -0
  139. package/dist/starting-doc/3_concept/2026_04_08_22_46_[METHODOLOGY]_diataxis_architecture_du_contenu.md +16 -0
  140. package/dist/starting-doc/4_reference/2026_04_08_23_14_[FUNDAMENTALS]_the_living_documentation_tool.md +41 -0
  141. package/dist/starting-doc/4_reference/2026_04_09_01_00_[REFERENCE]_raccourcis_clavier.md +61 -0
  142. package/dist/starting-doc/4_reference/2026_04_09_02_00_[REFERENCE]_tokens_pattern_nommage.md +75 -0
  143. package/dist/starting-doc/4_reference/2026_04_09_03_00_[REFERENCE]_types_de_snippets.md +68 -0
  144. package/dist/starting-doc/4_reference/2026_04_11_17_31_[FUNDAMENTALS]_architecturer_une_documentation.md +12 -0
  145. package/dist/starting-doc/4_reference/2026_04_12_14_07_[FUNDAMENTALS]_dossiers_et_catgories.md +89 -0
  146. package/dist/starting-doc/images/admin_screenshot.png +0 -0
  147. package/dist/starting-doc/images/ajout-document.png +0 -0
  148. package/dist/starting-doc/images/ajouter-document-categorie.png +0 -0
  149. package/dist/starting-doc/images/ajouter_un_document_dans_un_dossier.png +0 -0
  150. package/dist/starting-doc/images/architecturer_une_documentation_reference.png +0 -0
  151. package/dist/starting-doc/images/cr_er_un_document.png +0 -0
  152. package/dist/starting-doc/images/creation-nouveau-dossier.png +0 -0
  153. package/dist/starting-doc/images/creer-document-context-engineering.png +0 -0
  154. package/dist/starting-doc/images/creer-dossier-only-tutoriel.png +0 -0
  155. package/dist/starting-doc/images/creer-dossier-tutoriel.png +0 -0
  156. package/dist/starting-doc/images/creer-dossiers-done.png +0 -0
  157. package/dist/starting-doc/images/creer-un-document.png +0 -0
  158. package/dist/starting-doc/images/creer-vos-dossiers-tutoriel.png +0 -0
  159. package/dist/starting-doc/images/creer-vos-dossiers.png +0 -0
  160. package/dist/starting-doc/images/decouverte_adrs.png +0 -0
  161. package/dist/starting-doc/images/diataxis.png +0 -0
  162. package/dist/starting-doc/images/diataxis_callout.png +0 -0
  163. package/dist/starting-doc/images/document-cree.png +0 -0
  164. package/dist/starting-doc/images/liens_snippets.png +0 -0
  165. package/dist/starting-doc/images/living_documentation.png +0 -0
  166. package/dist/starting-doc/images/npm_logo.png +0 -0
  167. package/dist/starting-doc/images/popup-creer-document.png +0 -0
  168. package/dist/starting-doc/images/popup-creer-dossier.png +0 -0
  169. package/dist/starting-doc/images/popup-dossier-cree.png +0 -0
  170. package/dist/starting-doc/images/quatre-dossiers-crees.png +0 -0
  171. package/dist/starting-doc/images/screenshot-living-doc.png +0 -0
  172. package/dist/starting-doc/images/the_living_documentation_tool.png +0 -0
  173. package/package.json +49 -0
@@ -0,0 +1,2314 @@
1
+ <!doctype html>
2
+ <html lang="en" class="h-full">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Living Documentation</title>
7
+
8
+ <!-- i18n -->
9
+ <script src="/i18n.js"></script>
10
+
11
+ <!-- Tailwind CSS + Typography plugin via CDN -->
12
+ <script src="https://cdn.tailwindcss.com?plugins=typography"></script>
13
+
14
+ <!-- Highlight.js — syntax highlighting (always dark) -->
15
+ <link
16
+ rel="stylesheet"
17
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
18
+ />
19
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
20
+
21
+ <!-- Font Awesome — icons -->
22
+ <link
23
+ rel="stylesheet"
24
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
25
+ />
26
+
27
+ <!-- WordCloud2.js — vendored from npm, no CDN dependency -->
28
+ <script src="/vendor/wordcloud2.js"></script>
29
+ <!-- Word Cloud logic — stop words, browser, rendering -->
30
+ <script src="/wordcloud.js"></script>
31
+
32
+ <!-- Extracted modules (classic scripts, global symbols).
33
+ Use `defer` so they execute in order AFTER the DOM is parsed. -->
34
+ <script defer src="/utils.js"></script>
35
+ <script defer src="/state.js"></script>
36
+ <script defer src="/sidebar-helpers.js"></script>
37
+ <script defer src="/snippet-detect.js"></script>
38
+ <script defer src="/snippet-table.js"></script>
39
+ <script defer src="/snippet-tree.js"></script>
40
+ <script defer src="/dark-mode.js"></script>
41
+ <script defer src="/config.js"></script>
42
+ <script defer src="/sidebar.js"></script>
43
+ <script defer src="/search.js"></script>
44
+ <script defer src="/image-paste.js"></script>
45
+ <script defer src="/documents.js"></script>
46
+ <script defer src="/misc.js"></script>
47
+ <script defer src="/snippets.js"></script>
48
+ <script defer src="/annotations.js"></script>
49
+ <script defer src="/export.js"></script>
50
+ <script defer src="/diagram-link-modal.js"></script>
51
+ <script defer src="/new-folder-modal.js"></script>
52
+ <script defer src="/new-doc-modal.js"></script>
53
+ <script defer src="/boot.js"></script>
54
+
55
+ <script>
56
+ tailwind.config = {
57
+ darkMode: "class",
58
+ theme: { extend: {} },
59
+ };
60
+ </script>
61
+
62
+ <style>
63
+ /* Smooth sidebar transitions */
64
+ .category-docs {
65
+ transition:
66
+ max-height 0.2s ease,
67
+ opacity 0.2s ease;
68
+ }
69
+ .category-docs.collapsed {
70
+ max-height: 0;
71
+ opacity: 0;
72
+ overflow: hidden;
73
+ }
74
+ .category-docs.expanded {
75
+ max-height: 9999px;
76
+ opacity: 1;
77
+ }
78
+
79
+ /* Color swatch selection ring */
80
+ .color-swatch-btn.selected-swatch,
81
+ .color-text-swatch-btn.selected-text-swatch {
82
+ outline: 2px solid currentColor;
83
+ outline-offset: 2px;
84
+ box-shadow: 0 0 0 3px rgba(0,0,0,0.15);
85
+ }
86
+
87
+ /* Active doc highlight */
88
+ .doc-item.active {
89
+ background-color: rgba(59, 130, 246, 0.12);
90
+ color: rgb(59, 130, 246);
91
+ font-weight: 600;
92
+ }
93
+ .dark .doc-item.active {
94
+ background-color: rgba(96, 165, 250, 0.15);
95
+ color: rgb(96, 165, 250);
96
+ }
97
+
98
+ /* Prose overrides for correct dark mode */
99
+ .dark .prose {
100
+ color: #d1d5db;
101
+ }
102
+ .dark .prose h1,
103
+ .dark .prose h2,
104
+ .dark .prose h3,
105
+ .dark .prose h4,
106
+ .dark .prose h5,
107
+ .dark .prose h6 {
108
+ color: #f9fafb;
109
+ }
110
+ .dark .prose a {
111
+ color: #60a5fa;
112
+ }
113
+ .dark .prose strong {
114
+ color: #f3f4f6;
115
+ }
116
+ .prose code:not(pre code) {
117
+ background: #e5e7eb;
118
+ color: #111827;
119
+ padding: 0.1em 0.35em;
120
+ border-radius: 0.25em;
121
+ font-size: 0.875em;
122
+ font-weight: 400;
123
+ }
124
+ .dark .prose code:not(pre code) {
125
+ background: #4b5563;
126
+ color: #f87171;
127
+ padding: 0.1em 0.35em;
128
+ border-radius: 0.25em;
129
+ font-size: 0.875em;
130
+ font-weight: 400;
131
+ }
132
+
133
+ .dark .prose blockquote {
134
+ border-color: #4b5563;
135
+ color: #9ca3af;
136
+ }
137
+ .dark .prose hr {
138
+ border-color: #374151;
139
+ }
140
+ .dark .prose table th {
141
+ background: #1f2937;
142
+ color: #f9fafb;
143
+ }
144
+ .dark .prose table td {
145
+ border-color: #374151;
146
+ }
147
+ .dark .prose table tr:nth-child(even) {
148
+ background: #111827;
149
+ }
150
+
151
+ /* Print / Export PDF */
152
+ @media print {
153
+ #sidebar,
154
+ #header {
155
+ display: none !important;
156
+ }
157
+ #content-area {
158
+ margin: 0 !important;
159
+ }
160
+ .no-print {
161
+ display: none !important;
162
+ }
163
+ .prose {
164
+ max-width: 100% !important;
165
+ }
166
+ }
167
+
168
+ /* Scrollbar thin */
169
+ ::-webkit-scrollbar {
170
+ width: 6px;
171
+ height: 6px;
172
+ }
173
+ ::-webkit-scrollbar-track {
174
+ background: transparent;
175
+ }
176
+ ::-webkit-scrollbar-thumb {
177
+ background: #d1d5db;
178
+ border-radius: 3px;
179
+ }
180
+ .dark ::-webkit-scrollbar-thumb {
181
+ background: #4b5563;
182
+ }
183
+
184
+ /* Search highlight */
185
+ mark {
186
+ background: #fef08a;
187
+ color: inherit;
188
+ border-radius: 2px;
189
+ padding: 0 2px;
190
+ }
191
+ mark.match-active {
192
+ background: #f97316;
193
+ color: #fff;
194
+ }
195
+ .dark mark {
196
+ background: #713f12;
197
+ color: #fef9c3;
198
+ }
199
+ .dark mark.match-active {
200
+ background: #ea580c;
201
+ color: #fff;
202
+ }
203
+ </style>
204
+ </head>
205
+
206
+ <body
207
+ class="h-full bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100 transition-colors duration-200"
208
+ >
209
+ <div class="h-full flex flex-col">
210
+ <!-- ── Header ── -->
211
+ <header
212
+ id="header"
213
+ class="no-print flex items-center justify-between px-4 h-14 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shrink-0 z-10 shadow-sm"
214
+ >
215
+ <div class="flex items-center gap-3">
216
+ <span
217
+ onclick="toggleSidebar()"
218
+ data-i18n-title="nav.toggle_sidebar"
219
+ title="Toggle sidebar"
220
+ class="text-blue-600 dark:text-blue-400 text-xl select-none cursor-pointer hover:opacity-70 transition-opacity"
221
+ >&#128218;</span
222
+ >
223
+ <span id="app-title" class="font-semibold text-base tracking-tight"
224
+ >Living Documentation</span
225
+ >
226
+ </div>
227
+
228
+ <div class="flex items-center gap-2">
229
+ <!-- Search (desktop — mirrors sidebar search) -->
230
+ <div class="relative hidden sm:block">
231
+ <input
232
+ id="header-search"
233
+ type="search"
234
+ data-i18n-placeholder="nav.search_placeholder"
235
+ placeholder="Search docs…"
236
+ class="w-56 pl-8 pr-3 py-1.5 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
237
+ />
238
+ <span
239
+ class="absolute left-2.5 top-2 text-gray-400 text-sm pointer-events-none"
240
+ >&#128269;</span
241
+ >
242
+ </div>
243
+
244
+ <!-- Dark mode toggle -->
245
+ <button
246
+ id="dark-toggle"
247
+ data-i18n-title="nav.toggle_dark"
248
+ title="Toggle dark mode"
249
+ class="p-2 rounded-lg text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
250
+ >
251
+ <span id="dark-icon" class="text-lg leading-none">&#9790;</span>
252
+ </button>
253
+
254
+ <!-- Word Cloud -->
255
+ <button
256
+ onclick="openWordCloud()"
257
+ data-i18n="nav.word_cloud"
258
+ class="text-sm px-3 py-1.5 rounded-lg text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors font-medium"
259
+ >
260
+ &#9729; Word Cloud
261
+ </button>
262
+
263
+ <!-- Diagram link -->
264
+ <a
265
+ href="/diagram"
266
+ data-i18n="nav.diagram"
267
+ class="text-sm px-3 py-1.5 rounded-lg text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors font-medium"
268
+ >
269
+ &#9671; Diagram
270
+ </a>
271
+
272
+ <!-- Admin link -->
273
+ <a
274
+ href="/admin"
275
+ data-i18n="nav.admin"
276
+ class="text-sm px-3 py-1.5 rounded-lg text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors font-medium"
277
+ >
278
+ &#9881; Admin
279
+ </a>
280
+ </div>
281
+ </header>
282
+
283
+ <!-- ── Body ── -->
284
+ <div class="flex flex-1 overflow-hidden">
285
+ <!-- ── Sidebar ── -->
286
+ <aside
287
+ id="sidebar"
288
+ class="no-print w-72 shrink-0 flex flex-col border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 overflow-y-auto"
289
+ >
290
+ <!-- Mobile / sidebar search -->
291
+ <div class="p-3 border-b border-gray-100 dark:border-gray-800">
292
+ <div class="relative sm:hidden">
293
+ <input
294
+ id="sidebar-search"
295
+ type="search"
296
+ data-i18n-placeholder="nav.search_mobile_placeholder"
297
+ placeholder="Search…"
298
+ class="w-full pl-8 pr-3 py-1.5 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
299
+ />
300
+ <span
301
+ class="absolute left-2.5 top-2 text-gray-400 text-sm pointer-events-none"
302
+ >&#128269;</span
303
+ >
304
+ </div>
305
+
306
+ <!-- Stats -->
307
+ <div class="flex items-center justify-between mt-2 sm:mt-0">
308
+ <p
309
+ id="doc-count"
310
+ class="text-xs text-gray-400 dark:text-gray-500"
311
+ ></p>
312
+ <div class="flex items-center gap-2">
313
+ <button
314
+ onclick="openExportModal()"
315
+ data-i18n-title="nav.export"
316
+ title="Export"
317
+ class="text-gray-400 hover:text-blue-500 dark:text-gray-500 dark:hover:text-blue-400 transition-colors leading-none"
318
+ >
319
+ <i class="fa-solid fa-file-export"></i>
320
+ </button>
321
+ <button
322
+ onclick="openNewFolderModal()"
323
+ data-i18n-title="nav.new_folder"
324
+ title="New folder"
325
+ class="text-gray-400 hover:text-blue-500 dark:text-gray-500 dark:hover:text-blue-400 transition-colors leading-none"
326
+ >
327
+ <i class="fa-solid fa-folder-plus"></i>
328
+ </button>
329
+ <button
330
+ onclick="openNewDocModal()"
331
+ data-i18n-title="nav.new_document"
332
+ title="New document"
333
+ class="text-gray-400 hover:text-blue-500 dark:text-gray-500 dark:hover:text-blue-400 transition-colors leading-none"
334
+ >
335
+ <i class="fa-solid fa-file-circle-plus"></i>
336
+ </button>
337
+ </div>
338
+ </div>
339
+ </div>
340
+
341
+ <!-- Category tree -->
342
+ <nav id="category-tree" class="flex-1 py-2 space-y-0.5">
343
+ <p class="px-4 py-8 text-sm text-gray-400 text-center" data-i18n="common.loading">Loading…</p>
344
+ </nav>
345
+ </aside>
346
+
347
+ <!-- ── Main content ── -->
348
+ <main id="content-area" class="flex-1 overflow-y-auto">
349
+ <!-- Welcome / empty state -->
350
+ <div id="welcome" class="h-full flex items-center justify-center p-8">
351
+ <div class="max-w-md text-center">
352
+ <div class="text-5xl mb-4 select-none" aria-hidden="true">
353
+ &#128218;
354
+ </div>
355
+ <h2
356
+ data-i18n="welcome.title"
357
+ class="text-xl font-semibold text-gray-700 dark:text-gray-300 mb-2"
358
+ >
359
+ Select a document
360
+ </h2>
361
+ <p data-i18n="welcome.hint" class="text-sm text-gray-500 dark:text-gray-500">
362
+ Choose a document from the sidebar to start reading.
363
+ </p>
364
+ <p
365
+ id="welcome-pattern"
366
+ 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"
367
+ >
368
+ YYYY_MM_DD_HH_mm_[Category]_title.md
369
+ </p>
370
+ <p data-i18n="welcome.pattern_hint" class="mt-2 text-xs text-gray-400">
371
+ Expected filename pattern
372
+ </p>
373
+ </div>
374
+ </div>
375
+
376
+ <!-- Document viewer -->
377
+ <article id="doc-view" class="hidden max-w-4xl mx-auto px-6 py-8">
378
+ <!-- Doc header -->
379
+ <header
380
+ class="sticky top-0 z-10 bg-gray-50 dark:bg-gray-950 -mx-6 px-6 pt-8 mb-8 pb-6 border-b border-gray-300 dark:border-gray-800"
381
+ >
382
+ <div class="flex items-start gap-4 flex-wrap">
383
+ <div class="shrink min-w-0">
384
+ <div class="flex items-center gap-2 mb-2 flex-wrap">
385
+ <span
386
+ id="doc-breadcrumbs"
387
+ class="flex items-center gap-2 flex-wrap"
388
+ ></span>
389
+ <span
390
+ id="doc-date"
391
+ class="text-xs text-gray-400 dark:text-gray-500"
392
+ ></span>
393
+ </div>
394
+ <h1
395
+ id="doc-title"
396
+ class="text-2xl font-bold text-gray-900 dark:text-gray-50 leading-tight"
397
+ ></h1>
398
+ </div>
399
+ <!-- Actions -->
400
+ <div class="flex items-center gap-2 flex-wrap ml-auto">
401
+ <!-- View mode actions -->
402
+ <div id="view-actions" class="flex items-center gap-2">
403
+ <button
404
+ onclick="toggleMarker()"
405
+ id="stabilo-btn"
406
+ data-i18n-title="doc.marker_mode"
407
+ title="Mode Marker — surligner pour annoter"
408
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
409
+ >
410
+ <svg
411
+ id="stabilo-icon"
412
+ xmlns="http://www.w3.org/2000/svg"
413
+ width="18"
414
+ height="18"
415
+ viewBox="0 0 100 100"
416
+ fill="none"
417
+ style="
418
+ display: inline-block;
419
+ vertical-align: middle;
420
+ margin-right: 4px;
421
+ "
422
+ >
423
+ <!-- body -->
424
+ <rect
425
+ x="28"
426
+ y="10"
427
+ width="44"
428
+ height="52"
429
+ rx="6"
430
+ transform="rotate(40 50 50)"
431
+ fill="#bfdbfe"
432
+ stroke="currentColor"
433
+ stroke-width="6"
434
+ />
435
+ <!-- cap -->
436
+ <rect
437
+ x="52"
438
+ y="8"
439
+ width="22"
440
+ height="30"
441
+ rx="6"
442
+ transform="rotate(40 50 50)"
443
+ fill="#93c5fd"
444
+ stroke="currentColor"
445
+ stroke-width="6"
446
+ />
447
+ <!-- nib -->
448
+ <polygon
449
+ points="28,60 10,80 30,80 38,72"
450
+ fill="#93c5fd"
451
+ stroke="currentColor"
452
+ stroke-width="5"
453
+ stroke-linejoin="round"
454
+ />
455
+ <polygon
456
+ points="28,48 19,70 36,75 55,70"
457
+ fill="#93c5fd"
458
+ stroke="currentColor"
459
+ stroke-width="5"
460
+ stroke-linejoin="round"
461
+ />
462
+ <!-- underline stroke -->
463
+ <line
464
+ x1="10"
465
+ y1="90"
466
+ x2="72"
467
+ y2="90"
468
+ stroke="currentColor"
469
+ stroke-width="6"
470
+ stroke-linecap="round"
471
+ />
472
+ <!-- hidden cross (two diagonal bars) -->
473
+ <g id="stabilo-cross" style="display: none">
474
+ <line
475
+ x1="8"
476
+ y1="8"
477
+ x2="92"
478
+ y2="92"
479
+ stroke="currentColor"
480
+ stroke-width="8"
481
+ stroke-linecap="round"
482
+ />
483
+ <line
484
+ x1="92"
485
+ y1="8"
486
+ x2="8"
487
+ y2="92"
488
+ stroke="currentColor"
489
+ stroke-width="8"
490
+ stroke-linecap="round"
491
+ />
492
+ </g></svg
493
+ ><span data-i18n="doc.marker_btn">Marker</span>
494
+ </button>
495
+ <button
496
+ onclick="toggleFullWidth()"
497
+ id="full-width-btn"
498
+ data-i18n-title="doc.toggle_width"
499
+ title="Toggle full width"
500
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
501
+ >
502
+ <span data-i18n="doc.full_width_btn">&#8596; Full width</span>
503
+ </button>
504
+ <button
505
+ onclick="exportPDF()"
506
+ data-i18n-title="doc.export_pdf"
507
+ title="Export as PDF"
508
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
509
+ >
510
+ &#128196; <span data-i18n="doc.export_pdf_btn">Export PDF</span>
511
+ </button>
512
+ <button
513
+ onclick="copyLink()"
514
+ id="copy-link-btn"
515
+ data-i18n-title="doc.copy_link"
516
+ title="Copy link"
517
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
518
+ >
519
+ <i class="fa-solid fa-link"></i> <span data-i18n="doc.copy_link_btn">Copy link</span>
520
+ </button>
521
+ <button
522
+ onclick="enterEditMode()"
523
+ data-i18n-title="doc.edit"
524
+ title="Edit document"
525
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
526
+ >
527
+ <i class="fa-solid fa-file-pen"></i> <span data-i18n="doc.edit_btn">Edit</span>
528
+ </button>
529
+ <button
530
+ onclick="askDeleteDocument()"
531
+ data-i18n-title="doc.delete"
532
+ title="Delete document"
533
+ class="no-print text-sm px-3 py-1.5 rounded-lg border border-red-200 dark:border-red-700 text-red-500 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/40 transition-colors"
534
+ >
535
+ <i class="fa-solid fa-trash"></i> <span data-i18n="doc.delete_btn">Delete</span>
536
+ </button>
537
+ </div>
538
+ <!-- Edit mode actions -->
539
+ <div id="edit-actions" class="hidden flex items-center gap-2">
540
+ <span id="edit-save-msg" class="text-xs"></span>
541
+ <button
542
+ onclick="exitEditMode()"
543
+ data-i18n-title="doc.cancel_edit"
544
+ title="Cancel editing"
545
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
546
+ >
547
+ <span data-i18n="common.cancel">Cancel</span>
548
+ </button>
549
+ <button
550
+ onclick="openSnippetsModal()"
551
+ data-i18n-title="doc.snippets"
552
+ title="Insert a snippet"
553
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
554
+ >
555
+ <span data-i18n="doc.snippets_btn">🧩 Snippets</span>
556
+ </button>
557
+ <button
558
+ onclick="saveDocument()"
559
+ data-i18n-title="doc.save"
560
+ title="Save document"
561
+ class="text-sm px-4 py-1.5 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
562
+ >
563
+ <span data-i18n="common.save">Save</span>
564
+ </button>
565
+ </div>
566
+ </div>
567
+ </div>
568
+ <!-- Navigation history back-links -->
569
+ <div
570
+ id="doc-back"
571
+ class="hidden mt-2 flex flex-wrap gap-1 text-xs"
572
+ ></div>
573
+ <!-- Search match notice (inside sticky header) -->
574
+ <div
575
+ id="search-notice"
576
+ class="hidden mt-4 rounded-lg bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 text-sm text-yellow-800 dark:text-yellow-300 overflow-hidden"
577
+ >
578
+ <div
579
+ id="search-notice-title"
580
+ class="px-3 py-2 font-medium border-b border-yellow-200 dark:border-yellow-800"
581
+ ></div>
582
+ <ol
583
+ id="search-notice-list"
584
+ class="max-h-40 overflow-y-auto divide-y divide-yellow-100 dark:divide-yellow-900/40 list-none m-0 p-0"
585
+ ></ol>
586
+ </div>
587
+ </header>
588
+
589
+ <!-- Rendered markdown -->
590
+ <div
591
+ id="doc-content"
592
+ class="prose prose-gray dark:prose-invert max-w-none prose-headings:scroll-mt-4 prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-code:before:content-none prose-code:after:content-none prose-pre:bg-[#0d1117] prose-pre:border prose-pre:border-gray-700"
593
+ ></div>
594
+
595
+ <!-- Markdown editor -->
596
+ <textarea
597
+ id="doc-editor"
598
+ class="hidden w-full min-h-[70vh] px-4 py-3 text-sm font-mono leading-relaxed rounded-lg border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y"
599
+ spellcheck="false"
600
+ ></textarea>
601
+ </article>
602
+ </main>
603
+ </div>
604
+ <!-- end body row -->
605
+ </div>
606
+ <!-- end root -->
607
+
608
+ <!-- ── New Folder modal ── -->
609
+ <div
610
+ id="new-folder-modal"
611
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
612
+ >
613
+ <div
614
+ class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-md mx-4 p-6 space-y-4"
615
+ >
616
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
617
+ <svg
618
+ width="16"
619
+ height="16"
620
+ viewBox="0 0 20 20"
621
+ fill="none"
622
+ xmlns="http://www.w3.org/2000/svg"
623
+ class="inline-block mr-1 align-text-bottom"
624
+ >
625
+ <path
626
+ d="M2 5a2 2 0 012-2h3.586a1 1 0 01.707.293L9.707 4.707A1 1 0 0010.414 5H16a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V5z"
627
+ stroke="currentColor"
628
+ stroke-width="1.5"
629
+ fill="none"
630
+ />
631
+ <line
632
+ x1="10"
633
+ y1="8"
634
+ x2="10"
635
+ y2="14"
636
+ stroke="currentColor"
637
+ stroke-width="1.5"
638
+ stroke-linecap="round"
639
+ />
640
+ <line
641
+ x1="7"
642
+ y1="11"
643
+ x2="13"
644
+ y2="11"
645
+ stroke="currentColor"
646
+ stroke-width="1.5"
647
+ stroke-linecap="round"
648
+ />
649
+ </svg>
650
+ <span data-i18n="modal.new_folder.title">New Folder</span>
651
+ </h3>
652
+
653
+ <!-- Folder name -->
654
+ <div class="space-y-1.5">
655
+ <label
656
+ data-i18n="modal.new_folder.name_label"
657
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
658
+ >Folder name</label
659
+ >
660
+ <input
661
+ id="new-folder-name"
662
+ type="text"
663
+ data-i18n-placeholder="modal.new_folder.name_placeholder"
664
+ placeholder="my-folder"
665
+ oninput="newFolderUpdatePreview()"
666
+ 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"
667
+ />
668
+ </div>
669
+
670
+ <!-- Location -->
671
+ <div class="space-y-1.5">
672
+ <label
673
+ data-i18n="modal.new_folder.location_label"
674
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
675
+ >Location</label
676
+ >
677
+ <div class="flex items-center gap-2">
678
+ <span
679
+ id="new-folder-location-display"
680
+ class="flex-1 text-sm text-gray-600 dark:text-gray-300 font-mono truncate"
681
+ >/ (root)</span
682
+ >
683
+ <button
684
+ onclick="newFolderToggleBrowser()"
685
+ data-i18n="modal.new_folder.browse_btn"
686
+ class="text-xs px-2 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors shrink-0"
687
+ >
688
+ Browse…
689
+ </button>
690
+ </div>
691
+ <!-- Inline browser -->
692
+ <div
693
+ id="new-folder-browser"
694
+ class="hidden border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden text-sm"
695
+ >
696
+ <div
697
+ 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"
698
+ >
699
+ <button
700
+ id="new-folder-browse-up"
701
+ onclick="newFolderBrowseUp()"
702
+ class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 disabled:opacity-30"
703
+ >
704
+
705
+ </button>
706
+ <span
707
+ id="new-folder-browse-path"
708
+ class="text-xs text-gray-500 dark:text-gray-400 font-mono truncate flex-1"
709
+ ></span>
710
+ <button
711
+ onclick="newFolderSelectCurrentLocation()"
712
+ data-i18n="modal.new_folder.select_btn"
713
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline shrink-0"
714
+ >
715
+ Select this folder
716
+ </button>
717
+ </div>
718
+ <div
719
+ id="new-folder-browse-list"
720
+ class="max-h-40 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800"
721
+ ></div>
722
+ </div>
723
+ </div>
724
+
725
+ <!-- Preview -->
726
+ <div class="space-y-1">
727
+ <label
728
+ data-i18n="modal.new_folder.will_be_created"
729
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
730
+ >Will be created at</label
731
+ >
732
+ <p
733
+ id="new-folder-preview"
734
+ data-i18n="modal.new_folder.enter_name"
735
+ class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 truncate"
736
+ >
737
+ (enter a name)
738
+ </p>
739
+ </div>
740
+
741
+ <p id="new-folder-error" class="hidden text-xs text-red-500"></p>
742
+
743
+ <div class="flex justify-end gap-3 pt-1">
744
+ <button
745
+ onclick="closeNewFolderModal()"
746
+ data-i18n="common.cancel"
747
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
748
+ >
749
+ Cancel
750
+ </button>
751
+ <button
752
+ onclick="createNewFolder()"
753
+ id="new-folder-create-btn"
754
+ data-i18n="modal.new_folder.create_btn"
755
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
756
+ >
757
+ Create
758
+ </button>
759
+ </div>
760
+ </div>
761
+ </div>
762
+
763
+ <!-- ── New Document modal ── -->
764
+ <div
765
+ id="new-doc-modal"
766
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
767
+ >
768
+ <div
769
+ class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-lg mx-4 p-6 space-y-4"
770
+ >
771
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
772
+ &#10133; <span data-i18n="modal.new_doc.title">New Document</span>
773
+ </h3>
774
+
775
+ <!-- Title -->
776
+ <div class="space-y-1.5">
777
+ <label
778
+ data-i18n="modal.new_doc.title_label"
779
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
780
+ >Title</label
781
+ >
782
+ <input
783
+ id="new-doc-title"
784
+ type="text"
785
+ data-i18n-placeholder="modal.new_doc.title_placeholder"
786
+ placeholder="My document"
787
+ oninput="newDocUpdatePreview()"
788
+ 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"
789
+ />
790
+ </div>
791
+
792
+ <!-- Category -->
793
+ <div class="space-y-1.5">
794
+ <label
795
+ data-i18n="modal.new_doc.category_label"
796
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
797
+ >Category</label
798
+ >
799
+ <input
800
+ id="new-doc-category"
801
+ type="text"
802
+ placeholder="General"
803
+ oninput="newDocUpdatePreview()"
804
+ 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"
805
+ />
806
+ </div>
807
+
808
+ <!-- Location -->
809
+ <div class="space-y-1.5">
810
+ <label
811
+ data-i18n="modal.new_doc.location_label"
812
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
813
+ >Location</label
814
+ >
815
+ <div class="flex items-center gap-2">
816
+ <span
817
+ id="new-doc-folder-display"
818
+ class="flex-1 px-3 py-2 text-xs rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-500 dark:text-gray-400 font-mono truncate"
819
+ >
820
+ / (root)
821
+ </span>
822
+ <button
823
+ onclick="newDocToggleBrowser()"
824
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors shrink-0"
825
+ >
826
+ &#128193; <span data-i18n="modal.new_doc.browse_btn">Browse</span>
827
+ </button>
828
+ </div>
829
+ <!-- Inline folder browser -->
830
+ <div
831
+ id="new-doc-browser"
832
+ class="hidden border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden"
833
+ >
834
+ <div
835
+ 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"
836
+ >
837
+ <button
838
+ id="new-doc-browse-up"
839
+ onclick="newDocBrowseUp()"
840
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
841
+ >
842
+ &#8593; Up
843
+ </button>
844
+ <span
845
+ id="new-doc-browse-path"
846
+ class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
847
+ ></span>
848
+ </div>
849
+ <div
850
+ id="new-doc-browse-list"
851
+ class="divide-y divide-gray-100 dark:divide-gray-800 max-h-40 overflow-y-auto"
852
+ ></div>
853
+ <!-- Create new folder row -->
854
+ <div
855
+ class="border-t border-gray-200 dark:border-gray-700 flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800/50"
856
+ >
857
+ <input
858
+ id="new-doc-new-folder-name"
859
+ type="text"
860
+ data-i18n-placeholder="modal.new_doc.new_folder_placeholder"
861
+ placeholder="New folder name…"
862
+ class="flex-1 px-2 py-1 text-xs rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-blue-400"
863
+ />
864
+ <button
865
+ onclick="newDocCreateFolder()"
866
+ data-i18n="modal.new_doc.create_folder_btn"
867
+ class="text-xs px-2 py-1 rounded bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors shrink-0"
868
+ >
869
+ + Create
870
+ </button>
871
+ </div>
872
+ </div>
873
+ </div>
874
+
875
+ <!-- Filename preview -->
876
+ <div class="space-y-1.5">
877
+ <label
878
+ data-i18n="modal.new_doc.filename_label"
879
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
880
+ >Generated filename</label
881
+ >
882
+ <p
883
+ id="new-doc-filename-preview"
884
+ data-i18n="modal.new_doc.title_placeholder"
885
+ class="text-xs bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 font-mono text-gray-600 dark:text-gray-300 break-all"
886
+ >
887
+ (enter a title)
888
+ </p>
889
+ </div>
890
+
891
+ <!-- Error -->
892
+ <p id="new-doc-error" class="hidden text-xs text-red-500"></p>
893
+
894
+ <!-- Actions -->
895
+ <div class="flex justify-end gap-3 pt-1">
896
+ <button
897
+ onclick="closeNewDocModal()"
898
+ data-i18n="common.cancel"
899
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
900
+ >
901
+ Cancel
902
+ </button>
903
+ <button
904
+ onclick="createNewDocument()"
905
+ id="new-doc-create-btn"
906
+ data-i18n="common.create"
907
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
908
+ >
909
+ Create
910
+ </button>
911
+ </div>
912
+ </div>
913
+ </div>
914
+
915
+ <!-- ── Diagram link modal ── -->
916
+ <div
917
+ id="diag-link-modal"
918
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
919
+ >
920
+ <div
921
+ class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-lg mx-4 p-6 space-y-5"
922
+ >
923
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
924
+ <span data-i18n="modal.diag_link.title">&#9671; Link a diagram</span>
925
+ </h3>
926
+
927
+ <!-- Mode toggle -->
928
+ <div class="flex gap-4 text-sm">
929
+ <label class="flex items-center gap-2 cursor-pointer">
930
+ <input
931
+ type="radio"
932
+ name="diag-mode"
933
+ id="diag-mode-existing"
934
+ value="existing"
935
+ checked
936
+ onchange="diagModeChanged()"
937
+ />
938
+ <span data-i18n="modal.diag_link.existing_radio" class="text-gray-700 dark:text-gray-300"
939
+ >Existing diagram</span
940
+ >
941
+ </label>
942
+ <label class="flex items-center gap-2 cursor-pointer">
943
+ <input
944
+ type="radio"
945
+ name="diag-mode"
946
+ id="diag-mode-new"
947
+ value="new"
948
+ onchange="diagModeChanged()"
949
+ />
950
+ <span data-i18n="modal.diag_link.new_radio" class="text-gray-700 dark:text-gray-300">New diagram</span>
951
+ </label>
952
+ </div>
953
+
954
+ <!-- Existing diagram picker -->
955
+ <div id="diag-existing-section" class="space-y-1.5">
956
+ <label
957
+ data-i18n="modal.diag_link.select_label"
958
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
959
+ >Select diagram</label
960
+ >
961
+ <select
962
+ id="diag-select"
963
+ onchange="diagUpdatePreview()"
964
+ 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"
965
+ ></select>
966
+ </div>
967
+
968
+ <!-- New diagram name -->
969
+ <div id="diag-new-section" class="hidden space-y-1.5">
970
+ <label
971
+ data-i18n="modal.diag_link.name_label"
972
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
973
+ >Diagram name</label
974
+ >
975
+ <input
976
+ id="diag-new-name"
977
+ type="text"
978
+ data-i18n-placeholder="modal.diag_link.name_placeholder"
979
+ placeholder="My diagram"
980
+ oninput="diagUpdatePreview()"
981
+ 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"
982
+ />
983
+ </div>
984
+
985
+ <!-- Image filename -->
986
+ <div class="space-y-1.5">
987
+ <label
988
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
989
+ ><span data-i18n="modal.diag_link.img_label">Image filename</span>
990
+ <span data-i18n="modal.diag_link.img_hint" class="font-normal text-gray-400"
991
+ >(saved in ./images/)</span
992
+ ></label
993
+ >
994
+ <input
995
+ id="diag-img-name"
996
+ type="text"
997
+ placeholder="diagram.png"
998
+ oninput="diagUpdatePreview()"
999
+ 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 font-mono focus:outline-none focus:ring-2 focus:ring-blue-500"
1000
+ />
1001
+ </div>
1002
+
1003
+ <!-- Markdown preview -->
1004
+ <div class="space-y-1.5">
1005
+ <label
1006
+ data-i18n="modal.diag_link.markdown_label"
1007
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1008
+ >Markdown that will be appended</label
1009
+ >
1010
+ <pre
1011
+ id="diag-preview"
1012
+ class="text-xs bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 overflow-x-auto text-gray-700 dark:text-gray-300 whitespace-pre-wrap"
1013
+ ></pre>
1014
+ </div>
1015
+
1016
+ <!-- Actions -->
1017
+ <div class="flex justify-end gap-3 pt-1">
1018
+ <button
1019
+ onclick="closeDiagramLinkModal()"
1020
+ data-i18n="common.cancel"
1021
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
1022
+ >
1023
+ Cancel
1024
+ </button>
1025
+ <button
1026
+ onclick="insertDiagramLink()"
1027
+ id="diag-insert-btn"
1028
+ data-i18n="modal.diag_link.insert_btn"
1029
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
1030
+ >
1031
+ Insert &amp; Open Diagram
1032
+ </button>
1033
+ </div>
1034
+ </div>
1035
+ </div>
1036
+
1037
+ <!-- ── Snippets modal ── -->
1038
+ <div
1039
+ id="snippets-modal"
1040
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
1041
+ >
1042
+ <div
1043
+ class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-lg mx-4 p-6 space-y-5 max-h-[90vh] overflow-y-auto"
1044
+ >
1045
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
1046
+ <span data-i18n="snippet.modal_title">🧩 Insert a snippet</span>
1047
+ </h3>
1048
+
1049
+ <div
1050
+ id="snippet-detect-msg"
1051
+ class="hidden rounded-lg px-3 py-2 text-xs"
1052
+ ></div>
1053
+
1054
+ <!-- Snippet type selector -->
1055
+ <div class="space-y-1.5">
1056
+ <label
1057
+ data-i18n="snippet.type_label"
1058
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1059
+ >Snippet type</label
1060
+ >
1061
+ <select
1062
+ id="snippet-type"
1063
+ onchange="snippetTypeChanged()"
1064
+ 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"
1065
+ >
1066
+ <option data-i18n="snippet.diagram" value="diagram" selected>Diagram</option>
1067
+ <option data-i18n="snippet.collapsible" value="collapsible">Collapsible block (details)</option>
1068
+ <option data-i18n="snippet.link" value="link">Link</option>
1069
+ <option data-i18n="snippet.link_doc" value="doc-link">Link to document</option>
1070
+ <option data-i18n="snippet.link_anchor" value="anchor-link">Link to anchor</option>
1071
+ <option data-i18n="snippet.link_doc_anchor" value="anchor-doc-link">
1072
+ Link to document with anchor
1073
+ </option>
1074
+ <option data-i18n="snippet.numbered_list" value="ordered-list">Numbered list</option>
1075
+ <option data-i18n="snippet.bullet_list" value="unordered-list">Bullet list</option>
1076
+ <option data-i18n="snippet.code_block" value="code-block">Code block</option>
1077
+ <option data-i18n="snippet.blockquote" value="blockquote">Blockquote</option>
1078
+ <option data-i18n="snippet.separator" value="separator">Horizontal rule</option>
1079
+ <option data-i18n="snippet.image" value="image">Image</option>
1080
+ <option data-i18n="snippet.table" value="table">Table</option>
1081
+ <option data-i18n="snippet.tree" value="tree">Tree</option>
1082
+ <option data-i18n="snippet.colored_section" value="colored-section">Colored section</option>
1083
+ <option data-i18n="snippet.colored_text" value="colored-text">Colored text</option>
1084
+ </select>
1085
+ </div>
1086
+
1087
+ <!-- Panel: collapsible -->
1088
+ <div id="snip-panel-collapsible" class="space-y-3">
1089
+ <div class="space-y-1.5">
1090
+ <label
1091
+ data-i18n="snippet.collapsible_summary_label"
1092
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1093
+ >Summary title</label
1094
+ >
1095
+ <input
1096
+ id="snip-collapsible-summary"
1097
+ type="text"
1098
+ value="Details"
1099
+ oninput="snippetUpdatePreview()"
1100
+ 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"
1101
+ />
1102
+ </div>
1103
+ </div>
1104
+
1105
+ <!-- Panel: link -->
1106
+ <div id="snip-panel-link" class="hidden space-y-3">
1107
+ <div class="space-y-1.5">
1108
+ <label
1109
+ data-i18n="snippet.link_text_label"
1110
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1111
+ >Link text</label
1112
+ >
1113
+ <input
1114
+ id="snip-link-text"
1115
+ type="text"
1116
+ data-i18n-placeholder="snippet.link_text_placeholder"
1117
+ placeholder="My link"
1118
+ oninput="snippetUpdatePreview()"
1119
+ 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"
1120
+ />
1121
+ </div>
1122
+ <div class="space-y-1.5">
1123
+ <label
1124
+ data-i18n="snippet.link_url_label"
1125
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1126
+ >URL</label
1127
+ >
1128
+ <input
1129
+ id="snip-link-url"
1130
+ type="text"
1131
+ data-i18n-placeholder="snippet.link_url_placeholder"
1132
+ placeholder="https://..."
1133
+ oninput="snippetUpdatePreview()"
1134
+ 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"
1135
+ />
1136
+ </div>
1137
+ </div>
1138
+
1139
+ <!-- Panel: doc-link -->
1140
+ <div id="snip-panel-doc-link" class="hidden space-y-3">
1141
+ <div class="space-y-1.5">
1142
+ <label
1143
+ data-i18n="snippet.link_doc_label"
1144
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1145
+ >Document</label
1146
+ >
1147
+ <select
1148
+ id="snip-doc-select"
1149
+ onchange="snippetUpdatePreview()"
1150
+ 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"
1151
+ ></select>
1152
+ </div>
1153
+ <div class="space-y-1.5">
1154
+ <label
1155
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1156
+ ><span data-i18n="snippet.link_text_label">Link text</span>
1157
+ <span data-i18n="snippet.link_doc_text_hint" class="font-normal text-gray-400"
1158
+ >(optional, uses default title)</span
1159
+ ></label
1160
+ >
1161
+ <input
1162
+ id="snip-doc-link-text"
1163
+ type="text"
1164
+ placeholder=""
1165
+ oninput="snippetUpdatePreview()"
1166
+ 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"
1167
+ />
1168
+ </div>
1169
+ </div>
1170
+
1171
+ <!-- Panel: anchor-link -->
1172
+ <div id="snip-panel-anchor-link" class="hidden space-y-3">
1173
+ <div class="space-y-1.5">
1174
+ <label
1175
+ data-i18n="snippet.link_text_label"
1176
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1177
+ >Link text</label
1178
+ >
1179
+ <input
1180
+ id="snip-anchor-text"
1181
+ type="text"
1182
+ data-i18n-placeholder="snippet.link_section_placeholder"
1183
+ placeholder="View section"
1184
+ oninput="snippetUpdatePreview()"
1185
+ 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"
1186
+ />
1187
+ </div>
1188
+ <div class="space-y-1.5">
1189
+ <label
1190
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1191
+ ><span data-i18n="snippet.link_anchor_label">Anchor</span>
1192
+ <span data-i18n="snippet.link_anchor_hint" class="font-normal text-gray-400"
1193
+ >(without #, e.g. my-heading)</span
1194
+ ></label
1195
+ >
1196
+ <input
1197
+ id="snip-anchor-id"
1198
+ type="text"
1199
+ data-i18n-placeholder="snippet.link_anchor_placeholder"
1200
+ placeholder="my-heading"
1201
+ oninput="snippetUpdatePreview()"
1202
+ 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"
1203
+ />
1204
+ </div>
1205
+ </div>
1206
+
1207
+ <!-- Panel: anchor-doc-link -->
1208
+ <div id="snip-panel-anchor-doc-link" class="hidden space-y-3">
1209
+ <div class="space-y-1.5">
1210
+ <label
1211
+ data-i18n="snippet.link_target_doc_label"
1212
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1213
+ >Target document</label
1214
+ >
1215
+ <select
1216
+ id="snip-anchor-doc-select"
1217
+ onchange="snippetUpdatePreview()"
1218
+ 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"
1219
+ ></select>
1220
+ </div>
1221
+ <div class="space-y-1.5">
1222
+ <label
1223
+ data-i18n="snippet.link_text_label"
1224
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1225
+ >Link text</label
1226
+ >
1227
+ <input
1228
+ id="snip-anchor-doc-text"
1229
+ type="text"
1230
+ data-i18n-placeholder="snippet.link_section_placeholder"
1231
+ placeholder="View section"
1232
+ oninput="snippetUpdatePreview()"
1233
+ 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"
1234
+ />
1235
+ </div>
1236
+ <div class="space-y-1.5">
1237
+ <label
1238
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1239
+ ><span data-i18n="snippet.link_anchor_label">Anchor</span>
1240
+ <span data-i18n="snippet.link_anchor_hint" class="font-normal text-gray-400"
1241
+ >(without #, e.g. my-heading)</span
1242
+ ></label
1243
+ >
1244
+ <input
1245
+ id="snip-anchor-doc-id"
1246
+ type="text"
1247
+ data-i18n-placeholder="snippet.link_anchor_placeholder"
1248
+ placeholder="my-heading"
1249
+ oninput="snippetUpdatePreview()"
1250
+ 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"
1251
+ />
1252
+ </div>
1253
+ </div>
1254
+
1255
+ <!-- Panel: code-block -->
1256
+ <div id="snip-panel-code-block" class="hidden space-y-3">
1257
+ <div class="space-y-1.5">
1258
+ <label
1259
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1260
+ ><span data-i18n="snippet.code_lang_label">Language</span>
1261
+ <span data-i18n="snippet.code_lang_hint" class="font-normal text-gray-400"
1262
+ >(e.g. javascript, python, bash…)</span
1263
+ ></label
1264
+ >
1265
+ <input
1266
+ id="snip-code-lang"
1267
+ type="text"
1268
+ placeholder="javascript"
1269
+ oninput="snippetUpdatePreview()"
1270
+ 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"
1271
+ />
1272
+ </div>
1273
+ </div>
1274
+
1275
+ <!-- Panel: image -->
1276
+ <div id="snip-panel-image" class="hidden space-y-3">
1277
+ <div class="space-y-1.5">
1278
+ <label
1279
+ data-i18n="snippet.image_alt_label"
1280
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1281
+ >Alt text</label
1282
+ >
1283
+ <input
1284
+ id="snip-image-alt"
1285
+ type="text"
1286
+ data-i18n-placeholder="snippet.image_alt_placeholder"
1287
+ placeholder="Image description"
1288
+ oninput="snippetUpdatePreview()"
1289
+ 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"
1290
+ />
1291
+ </div>
1292
+ <div class="space-y-1.5">
1293
+ <label
1294
+ data-i18n="snippet.image_url_label"
1295
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1296
+ >Image URL</label
1297
+ >
1298
+ <input
1299
+ id="snip-image-url"
1300
+ type="text"
1301
+ data-i18n-placeholder="snippet.image_src_placeholder"
1302
+ placeholder="./images/my-image.png"
1303
+ oninput="snippetUpdatePreview()"
1304
+ 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"
1305
+ />
1306
+ </div>
1307
+ </div>
1308
+
1309
+ <!-- Panel: table -->
1310
+ <div id="snip-panel-table" class="hidden space-y-3">
1311
+ <div class="flex items-center gap-5">
1312
+ <div class="flex items-center gap-2">
1313
+ <span data-i18n="snippet.table_rows_label" class="text-xs text-gray-500 dark:text-gray-400"
1314
+ >Rows</span
1315
+ >
1316
+ <button
1317
+ onclick="tableChangeRows(-1)"
1318
+ class="text-xs px-2 py-0.5 rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
1319
+ >
1320
+
1321
+ </button>
1322
+ <span
1323
+ id="snip-table-rows"
1324
+ class="text-xs font-mono w-4 text-center"
1325
+ >3</span
1326
+ >
1327
+ <button
1328
+ onclick="tableChangeRows(1)"
1329
+ class="text-xs px-2 py-0.5 rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
1330
+ >
1331
+ +
1332
+ </button>
1333
+ </div>
1334
+ <div class="flex items-center gap-2">
1335
+ <span data-i18n="snippet.table_cols_label" class="text-xs text-gray-500 dark:text-gray-400"
1336
+ >Columns</span
1337
+ >
1338
+ <button
1339
+ onclick="tableChangeCols(-1)"
1340
+ class="text-xs px-2 py-0.5 rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
1341
+ >
1342
+
1343
+ </button>
1344
+ <span
1345
+ id="snip-table-cols"
1346
+ class="text-xs font-mono w-4 text-center"
1347
+ >3</span
1348
+ >
1349
+ <button
1350
+ onclick="tableChangeCols(1)"
1351
+ class="text-xs px-2 py-0.5 rounded border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
1352
+ >
1353
+ +
1354
+ </button>
1355
+ </div>
1356
+ </div>
1357
+ <div id="snip-table-grid" class="overflow-x-auto"></div>
1358
+ </div>
1359
+
1360
+ <!-- Panel: tree -->
1361
+ <div id="snip-panel-tree" class="hidden space-y-2">
1362
+ <p data-i18n="snippet.tree_hint" class="text-xs text-gray-400 dark:text-gray-500">
1363
+ ← → to indent · names with / will be folders
1364
+ </p>
1365
+ <div id="snip-tree-list" class="space-y-1"></div>
1366
+ <button
1367
+ onclick="treeAddItem()"
1368
+ data-i18n="snippet.tree_add_btn"
1369
+ class="text-xs px-3 py-1.5 rounded-lg border border-dashed border-gray-300 dark:border-gray-600 text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors w-full"
1370
+ >
1371
+ + Add item
1372
+ </button>
1373
+ </div>
1374
+
1375
+ <!-- Panel: diagram -->
1376
+ <div id="snip-panel-diagram" class="hidden space-y-3">
1377
+ <!-- Mode toggle -->
1378
+ <div class="flex gap-4 text-sm">
1379
+ <label class="flex items-center gap-2 cursor-pointer">
1380
+ <input
1381
+ type="radio"
1382
+ name="snip-diag-mode"
1383
+ id="snip-diag-mode-existing"
1384
+ value="existing"
1385
+ checked
1386
+ onchange="snippetDiagModeChanged()"
1387
+ />
1388
+ <span data-i18n="snippet.diagram_existing" class="text-gray-700 dark:text-gray-300"
1389
+ >Existing diagram</span
1390
+ >
1391
+ </label>
1392
+ <label class="flex items-center gap-2 cursor-pointer">
1393
+ <input
1394
+ type="radio"
1395
+ name="snip-diag-mode"
1396
+ id="snip-diag-mode-new"
1397
+ value="new"
1398
+ onchange="snippetDiagModeChanged()"
1399
+ />
1400
+ <span data-i18n="snippet.diagram_new" class="text-gray-700 dark:text-gray-300"
1401
+ >New diagram</span
1402
+ >
1403
+ </label>
1404
+ </div>
1405
+ <!-- Existing picker -->
1406
+ <div id="snip-diag-existing-section" class="space-y-1.5">
1407
+ <label
1408
+ data-i18n="snippet.diagram_select_label"
1409
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1410
+ >Select diagram</label
1411
+ >
1412
+ <select
1413
+ id="snip-diag-select"
1414
+ onchange="
1415
+ snippetDiagSyncImgName();
1416
+ snippetUpdatePreview();
1417
+ "
1418
+ 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"
1419
+ ></select>
1420
+ </div>
1421
+ <!-- New diagram name -->
1422
+ <div id="snip-diag-new-section" class="hidden space-y-1.5">
1423
+ <label
1424
+ data-i18n="snippet.diagram_name_label"
1425
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1426
+ >Diagram name</label
1427
+ >
1428
+ <input
1429
+ id="snip-diag-new-name"
1430
+ type="text"
1431
+ data-i18n-placeholder="snippet.diagram_name_placeholder"
1432
+ placeholder="Diagram name…"
1433
+ oninput="
1434
+ snippetDiagSyncImgName();
1435
+ snippetUpdatePreview();
1436
+ "
1437
+ 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"
1438
+ />
1439
+ </div>
1440
+ <!-- Image filename -->
1441
+ <div class="space-y-1.5">
1442
+ <label
1443
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1444
+ >
1445
+ <span data-i18n="snippet.diag_img_label">Image filename</span>
1446
+ <span data-i18n="snippet.diag_img_saved_hint" class="font-normal text-gray-400"
1447
+ >(saved in ./images/)</span
1448
+ >
1449
+ </label>
1450
+ <input
1451
+ id="snip-diag-img-name"
1452
+ type="text"
1453
+ placeholder="diagram.png"
1454
+ oninput="snippetUpdatePreview()"
1455
+ 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 font-mono focus:outline-none focus:ring-2 focus:ring-blue-500"
1456
+ />
1457
+ </div>
1458
+ </div>
1459
+
1460
+ <!-- Panel: colored-section -->
1461
+ <div id="snip-panel-colored-section" class="hidden space-y-3">
1462
+ <div class="space-y-1.5">
1463
+ <label
1464
+ data-i18n="snippet.colored_section_color_label"
1465
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1466
+ >Color</label
1467
+ >
1468
+ <div class="flex flex-wrap gap-2">
1469
+ <button
1470
+ type="button"
1471
+ data-color-swatch="info"
1472
+ onclick="colorSectionPickSwatch(this)"
1473
+ data-i18n-title="snippet.colored_section_swatch_info"
1474
+ title="Info (blue)"
1475
+ class="w-7 h-7 rounded-full border-2 border-blue-400 bg-blue-100 ring-2 ring-transparent hover:ring-blue-400 transition-all color-swatch-btn selected-swatch"
1476
+ ></button>
1477
+ <button
1478
+ type="button"
1479
+ data-color-swatch="success"
1480
+ onclick="colorSectionPickSwatch(this)"
1481
+ data-i18n-title="snippet.colored_section_swatch_success"
1482
+ title="Success (green)"
1483
+ class="w-7 h-7 rounded-full border-2 border-green-400 bg-green-100 ring-2 ring-transparent hover:ring-green-400 transition-all color-swatch-btn"
1484
+ ></button>
1485
+ <button
1486
+ type="button"
1487
+ data-color-swatch="warning"
1488
+ onclick="colorSectionPickSwatch(this)"
1489
+ data-i18n-title="snippet.colored_section_swatch_warning"
1490
+ title="Warning (amber)"
1491
+ class="w-7 h-7 rounded-full border-2 border-amber-400 bg-amber-100 ring-2 ring-transparent hover:ring-amber-400 transition-all color-swatch-btn"
1492
+ ></button>
1493
+ <button
1494
+ type="button"
1495
+ data-color-swatch="danger"
1496
+ onclick="colorSectionPickSwatch(this)"
1497
+ data-i18n-title="snippet.colored_section_swatch_danger"
1498
+ title="Danger (red)"
1499
+ class="w-7 h-7 rounded-full border-2 border-red-400 bg-red-100 ring-2 ring-transparent hover:ring-red-400 transition-all color-swatch-btn"
1500
+ ></button>
1501
+ <button
1502
+ type="button"
1503
+ data-color-swatch="note"
1504
+ onclick="colorSectionPickSwatch(this)"
1505
+ data-i18n-title="snippet.colored_section_swatch_note"
1506
+ title="Note (purple)"
1507
+ class="w-7 h-7 rounded-full border-2 border-purple-400 bg-purple-100 ring-2 ring-transparent hover:ring-purple-400 transition-all color-swatch-btn"
1508
+ ></button>
1509
+ <button
1510
+ type="button"
1511
+ data-color-swatch="neutral"
1512
+ onclick="colorSectionPickSwatch(this)"
1513
+ data-i18n-title="snippet.colored_section_swatch_neutral"
1514
+ title="Neutral (gray)"
1515
+ class="w-7 h-7 rounded-full border-2 border-gray-400 bg-gray-100 ring-2 ring-transparent hover:ring-gray-400 transition-all color-swatch-btn"
1516
+ ></button>
1517
+ </div>
1518
+ </div>
1519
+ <div class="space-y-1.5">
1520
+ <label
1521
+ data-i18n="snippet.colored_section_content_label"
1522
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1523
+ >Content</label
1524
+ >
1525
+ <textarea
1526
+ id="snip-colored-content"
1527
+ rows="4"
1528
+ data-i18n-placeholder="snippet.colored_section_content_placeholder"
1529
+ placeholder="Your text here…"
1530
+ oninput="snippetUpdatePreview()"
1531
+ 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 resize-y font-mono"
1532
+ ></textarea>
1533
+ </div>
1534
+ </div>
1535
+
1536
+ <!-- Panel: colored-text -->
1537
+ <div id="snip-panel-colored-text" class="hidden space-y-3">
1538
+ <div class="space-y-1.5">
1539
+ <label
1540
+ data-i18n="snippet.colored_text_color_label"
1541
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1542
+ >Color</label
1543
+ >
1544
+ <div class="flex flex-wrap gap-2">
1545
+ <button
1546
+ type="button"
1547
+ data-color-text-swatch="info"
1548
+ onclick="colorTextPickSwatch(this)"
1549
+ data-i18n-title="snippet.colored_section_swatch_info"
1550
+ title="Info (blue)"
1551
+ class="w-7 h-7 rounded-full border-2 border-blue-500 bg-blue-500 ring-2 ring-transparent hover:ring-blue-400 transition-all color-text-swatch-btn selected-text-swatch"
1552
+ ></button>
1553
+ <button
1554
+ type="button"
1555
+ data-color-text-swatch="success"
1556
+ onclick="colorTextPickSwatch(this)"
1557
+ data-i18n-title="snippet.colored_section_swatch_success"
1558
+ title="Success (green)"
1559
+ class="w-7 h-7 rounded-full border-2 border-green-500 bg-green-500 ring-2 ring-transparent hover:ring-green-400 transition-all color-text-swatch-btn"
1560
+ ></button>
1561
+ <button
1562
+ type="button"
1563
+ data-color-text-swatch="warning"
1564
+ onclick="colorTextPickSwatch(this)"
1565
+ data-i18n-title="snippet.colored_section_swatch_warning"
1566
+ title="Warning (amber)"
1567
+ class="w-7 h-7 rounded-full border-2 border-amber-500 bg-amber-500 ring-2 ring-transparent hover:ring-amber-400 transition-all color-text-swatch-btn"
1568
+ ></button>
1569
+ <button
1570
+ type="button"
1571
+ data-color-text-swatch="danger"
1572
+ onclick="colorTextPickSwatch(this)"
1573
+ data-i18n-title="snippet.colored_section_swatch_danger"
1574
+ title="Danger (red)"
1575
+ class="w-7 h-7 rounded-full border-2 border-red-500 bg-red-500 ring-2 ring-transparent hover:ring-red-400 transition-all color-text-swatch-btn"
1576
+ ></button>
1577
+ <button
1578
+ type="button"
1579
+ data-color-text-swatch="note"
1580
+ onclick="colorTextPickSwatch(this)"
1581
+ data-i18n-title="snippet.colored_section_swatch_note"
1582
+ title="Note (purple)"
1583
+ class="w-7 h-7 rounded-full border-2 border-purple-500 bg-purple-500 ring-2 ring-transparent hover:ring-purple-400 transition-all color-text-swatch-btn"
1584
+ ></button>
1585
+ <button
1586
+ type="button"
1587
+ data-color-text-swatch="neutral"
1588
+ onclick="colorTextPickSwatch(this)"
1589
+ data-i18n-title="snippet.colored_section_swatch_neutral"
1590
+ title="Neutral (gray)"
1591
+ class="w-7 h-7 rounded-full border-2 border-gray-500 bg-gray-500 ring-2 ring-transparent hover:ring-gray-400 transition-all color-text-swatch-btn"
1592
+ ></button>
1593
+ </div>
1594
+ </div>
1595
+ <div class="space-y-1.5">
1596
+ <label
1597
+ data-i18n="snippet.colored_text_content_label"
1598
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1599
+ >Text</label
1600
+ >
1601
+ <input
1602
+ id="snip-colored-text-content"
1603
+ type="text"
1604
+ data-i18n-placeholder="snippet.colored_text_content_placeholder"
1605
+ placeholder="Your text…"
1606
+ oninput="snippetUpdatePreview()"
1607
+ 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"
1608
+ />
1609
+ </div>
1610
+ </div>
1611
+
1612
+ <!-- Markdown preview -->
1613
+ <div class="space-y-1.5">
1614
+ <label
1615
+ data-i18n="snippet.markdown_preview_label"
1616
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1617
+ >Markdown preview</label
1618
+ >
1619
+ <pre
1620
+ id="snippet-preview"
1621
+ class="text-xs bg-gray-100 dark:bg-gray-800 rounded-lg px-3 py-2 overflow-x-auto text-gray-700 dark:text-gray-300 whitespace-pre-wrap"
1622
+ ></pre>
1623
+ </div>
1624
+
1625
+ <!-- Actions -->
1626
+ <div class="flex justify-end gap-3 pt-1">
1627
+ <button
1628
+ onclick="closeSnippetsModal()"
1629
+ data-i18n="common.cancel"
1630
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
1631
+ >
1632
+ Cancel
1633
+ </button>
1634
+ <button
1635
+ onclick="insertSnippet()"
1636
+ data-i18n="snippet.insert_btn"
1637
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
1638
+ >
1639
+ Insert
1640
+ </button>
1641
+ </div>
1642
+ </div>
1643
+ </div>
1644
+
1645
+ <!-- ── Image paste confirmation modal ── -->
1646
+ <div
1647
+ id="img-paste-modal"
1648
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
1649
+ >
1650
+ <div
1651
+ class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-sm mx-4 p-6 space-y-5"
1652
+ >
1653
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
1654
+ 🖼️ <span data-i18n="modal.img_paste.title">Paste clipboard image?</span>
1655
+ </h3>
1656
+ <div class="space-y-1.5">
1657
+ <label
1658
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1659
+ >
1660
+ <span data-i18n="modal.img_paste.filename_label">Filename</span>
1661
+ <span data-i18n="modal.img_paste.saved_hint" class="font-normal text-gray-400"
1662
+ >(saved in images/)</span
1663
+ >
1664
+ </label>
1665
+ <div class="flex items-center gap-2">
1666
+ <input
1667
+ id="img-paste-name"
1668
+ type="text"
1669
+ class="flex-1 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 font-mono focus:outline-none focus:ring-2 focus:ring-blue-500"
1670
+ />
1671
+ <span
1672
+ id="img-paste-ext"
1673
+ class="text-xs text-gray-400 shrink-0"
1674
+ ></span>
1675
+ </div>
1676
+ </div>
1677
+ <div class="flex justify-end gap-3 pt-1">
1678
+ <button
1679
+ onclick="imgPasteCancel()"
1680
+ data-i18n="common.cancel"
1681
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
1682
+ >
1683
+ Cancel
1684
+ </button>
1685
+ <button
1686
+ onclick="imgPasteConfirm()"
1687
+ data-i18n="modal.img_paste.paste_btn"
1688
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
1689
+ >
1690
+ Paste
1691
+ </button>
1692
+ </div>
1693
+ </div>
1694
+ </div>
1695
+
1696
+ <!-- ── Marker: post-it creation popup ── -->
1697
+ <div
1698
+ id="stabilo-popup"
1699
+ class="hidden fixed z-50 w-80 bg-yellow-50 dark:bg-yellow-900/80 border-2 border-yellow-300 dark:border-yellow-600 rounded-xl shadow-2xl p-4 flex flex-col gap-3"
1700
+ style="top: 0; left: 0"
1701
+ >
1702
+ <div
1703
+ class="text-xs font-semibold text-yellow-700 dark:text-yellow-300 flex items-center gap-2"
1704
+ >
1705
+ <span>&#9999;</span> <span data-i18n="annotation.title">Annotation</span>
1706
+ </div>
1707
+ <div
1708
+ id="stabilo-selected-preview"
1709
+ class="text-xs text-yellow-800 dark:text-yellow-200 bg-yellow-100 dark:bg-yellow-800/50 rounded px-2 py-1 italic max-h-16 overflow-y-auto border border-yellow-200 dark:border-yellow-700"
1710
+ ></div>
1711
+ <textarea
1712
+ id="stabilo-note-input"
1713
+ rows="4"
1714
+ data-i18n-placeholder="annotation.placeholder"
1715
+ placeholder="Your comment or suggestion…"
1716
+ class="w-full text-sm rounded-lg border border-yellow-300 dark:border-yellow-600 bg-white dark:bg-yellow-950 text-gray-900 dark:text-gray-100 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-yellow-400 resize-none"
1717
+ >
1718
+ </textarea>
1719
+ <div class="flex justify-end gap-2">
1720
+ <button
1721
+ onclick="cancelMarkerPopup()"
1722
+ data-i18n="common.cancel"
1723
+ class="text-sm px-3 py-1.5 rounded-lg border border-yellow-300 dark:border-yellow-600 text-yellow-700 dark:text-yellow-300 hover:bg-yellow-100 dark:hover:bg-yellow-800 transition-colors"
1724
+ >
1725
+ Cancel
1726
+ </button>
1727
+ <button
1728
+ onclick="saveAnnotation()"
1729
+ data-i18n="annotation.save_btn"
1730
+ class="text-sm px-4 py-1.5 rounded-lg bg-yellow-400 hover:bg-yellow-500 dark:bg-yellow-600 dark:hover:bg-yellow-500 text-yellow-900 dark:text-white font-semibold transition-colors"
1731
+ >
1732
+ Save
1733
+ </button>
1734
+ </div>
1735
+ </div>
1736
+
1737
+ <!-- ── Marker: read-only post-it hover popup ── -->
1738
+ <div
1739
+ id="stabilo-read-popup"
1740
+ class="hidden fixed z-50 w-80 bg-yellow-50 dark:bg-yellow-900/80 border-2 border-yellow-300 dark:border-yellow-600 rounded-xl shadow-2xl p-4 flex flex-col gap-2 pointer-events-auto"
1741
+ style="top: 0; left: 0"
1742
+ >
1743
+ <div
1744
+ class="text-xs font-semibold text-yellow-700 dark:text-yellow-300 flex items-center gap-2"
1745
+ >
1746
+ <span>&#9999;</span>
1747
+ <span
1748
+ id="stabilo-read-date"
1749
+ class="font-normal text-yellow-600 dark:text-yellow-400"
1750
+ ></span>
1751
+ </div>
1752
+ <div
1753
+ id="stabilo-read-orphan"
1754
+ data-i18n="annotation.orphan"
1755
+ class="hidden text-xs font-semibold text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 rounded px-2 py-1"
1756
+ ></div>
1757
+ <div
1758
+ id="stabilo-read-text"
1759
+ class="text-sm text-gray-800 dark:text-gray-100 whitespace-pre-wrap max-h-48 overflow-y-auto"
1760
+ ></div>
1761
+ <div
1762
+ class="flex justify-end pt-1 border-t border-yellow-200 dark:border-yellow-700"
1763
+ >
1764
+ <button
1765
+ id="stabilo-delete-btn"
1766
+ data-i18n="annotation.delete_btn"
1767
+ class="text-xs px-3 py-1.5 rounded-lg border border-red-200 dark:border-red-700 text-red-500 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/40 transition-colors flex items-center gap-1"
1768
+ >
1769
+ 🗑 Delete
1770
+ </button>
1771
+ </div>
1772
+ </div>
1773
+
1774
+ <!-- ── Marker: delete confirmation ── -->
1775
+ <div
1776
+ id="stabilo-confirm-delete"
1777
+ class="hidden fixed z-50 w-72 bg-white dark:bg-gray-900 border border-red-200 dark:border-red-700 rounded-xl shadow-2xl p-4 flex flex-col gap-3"
1778
+ style="top: 0; left: 0"
1779
+ >
1780
+ <p data-i18n="annotation.confirm_delete" class="text-sm text-gray-700 dark:text-gray-200">
1781
+ Delete this annotation?
1782
+ </p>
1783
+ <div class="flex justify-end gap-2">
1784
+ <button
1785
+ onclick="cancelDeleteAnnotation()"
1786
+ data-i18n="common.cancel"
1787
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
1788
+ >
1789
+ Cancel
1790
+ </button>
1791
+ <button
1792
+ onclick="confirmDeleteAnnotation()"
1793
+ data-i18n="annotation.confirm_btn"
1794
+ class="text-sm px-4 py-1.5 rounded-lg bg-red-500 hover:bg-red-600 text-white font-semibold transition-colors"
1795
+ >
1796
+ Delete
1797
+ </button>
1798
+ </div>
1799
+ </div>
1800
+
1801
+ <!-- ── Document: delete confirmation ── -->
1802
+ <div
1803
+ id="doc-confirm-delete"
1804
+ class="hidden fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4"
1805
+ onclick="cancelDeleteDocument(event)"
1806
+ >
1807
+ <div
1808
+ class="w-full max-w-sm bg-white dark:bg-gray-900 border border-red-200 dark:border-red-700 rounded-xl shadow-2xl p-5 flex flex-col gap-3"
1809
+ onclick="event.stopPropagation()"
1810
+ >
1811
+ <p data-i18n="doc.confirm_delete" class="text-sm text-gray-700 dark:text-gray-200">
1812
+ Permanently delete this document?
1813
+ </p>
1814
+ <p id="doc-confirm-delete-title" class="text-xs text-gray-500 dark:text-gray-400 italic truncate"></p>
1815
+ <div class="flex justify-end gap-2 mt-2">
1816
+ <button
1817
+ onclick="cancelDeleteDocument()"
1818
+ data-i18n="common.cancel"
1819
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
1820
+ >
1821
+ Cancel
1822
+ </button>
1823
+ <button
1824
+ onclick="confirmDeleteDocument()"
1825
+ data-i18n="doc.confirm_delete_btn"
1826
+ class="text-sm px-4 py-1.5 rounded-lg bg-red-500 hover:bg-red-600 text-white font-semibold transition-colors"
1827
+ >
1828
+ Delete
1829
+ </button>
1830
+ </div>
1831
+ </div>
1832
+ </div>
1833
+
1834
+ <!-- ── Marker: right elevator ── -->
1835
+ <div
1836
+ id="stabilo-elevator"
1837
+ class="hidden fixed top-14 right-0 w-10 flex flex-col items-center py-2 gap-2 z-40 overflow-y-auto"
1838
+ style="max-height: calc(100vh - 3.5rem)"
1839
+ ></div>
1840
+
1841
+ <!-- ── Image lightbox overlay ── -->
1842
+ <div
1843
+ id="img-lightbox"
1844
+ class="hidden fixed inset-0 z-50 bg-black/90 flex items-center justify-center cursor-pointer"
1845
+ onclick="closeLightbox()"
1846
+ >
1847
+ <img
1848
+ id="img-lightbox-img"
1849
+ src=""
1850
+ alt=""
1851
+ class="max-w-full max-h-full object-contain select-none"
1852
+ onclick="event.stopPropagation()"
1853
+ />
1854
+ <button
1855
+ onclick="closeLightbox()"
1856
+ class="absolute top-4 right-4 text-white/70 hover:text-white text-2xl leading-none"
1857
+ >
1858
+ &times;
1859
+ </button>
1860
+ </div>
1861
+
1862
+ <!-- ── Word Cloud overlay ── -->
1863
+ <div
1864
+ id="wc-overlay"
1865
+ class="hidden fixed inset-0 z-50 flex flex-col bg-white dark:bg-gray-950"
1866
+ >
1867
+ <div
1868
+ class="flex items-center justify-between px-6 h-14 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shrink-0"
1869
+ >
1870
+ <h2 data-i18n="wc.title" class="font-semibold text-base">&#9729; Word Cloud</h2>
1871
+ <button
1872
+ onclick="closeWordCloud()"
1873
+ data-i18n="wc.close_btn"
1874
+ class="text-sm px-3 py-1.5 rounded-lg text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
1875
+ >
1876
+ ✕ Close
1877
+ </button>
1878
+ </div>
1879
+ <!-- Search root toolbar -->
1880
+ <div
1881
+ class="border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shrink-0"
1882
+ >
1883
+ <!-- Row 1: path + browse + launch -->
1884
+ <div class="flex items-center gap-3 px-6 py-3">
1885
+ <label
1886
+ data-i18n="wc.search_root_label"
1887
+ class="text-xs font-medium text-gray-500 dark:text-gray-400 shrink-0"
1888
+ >Search root</label
1889
+ >
1890
+ <input
1891
+ id="wc-root"
1892
+ type="text"
1893
+ readonly
1894
+ spellcheck="false"
1895
+ class="flex-1 px-3 py-1.5 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 font-mono cursor-default focus:outline-none"
1896
+ />
1897
+ <button
1898
+ onclick="wcToggleBrowser()"
1899
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors shrink-0"
1900
+ >
1901
+ &#128193; <span data-i18n="wc.browse_btn">Browse</span>
1902
+ </button>
1903
+ <button
1904
+ onclick="launchWordCloud()"
1905
+ data-i18n="wc.launch_btn"
1906
+ class="text-sm px-4 py-1.5 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors shrink-0"
1907
+ >
1908
+ &#9654; Launch
1909
+ </button>
1910
+ </div>
1911
+ <!-- Row 1b: exclude folders -->
1912
+ <div class="flex items-start gap-3 px-6 pb-2">
1913
+ <label
1914
+ data-i18n="wc.exclude_label"
1915
+ class="text-xs font-medium text-gray-500 dark:text-gray-400 shrink-0 pt-1.5"
1916
+ >Exclude</label
1917
+ >
1918
+ <textarea
1919
+ id="wc-exclude"
1920
+ rows="3"
1921
+ spellcheck="false"
1922
+ data-i18n-placeholder="wc.exclude_placeholder"
1923
+ placeholder="components/ui, package-lock.json (one per line or comma-separated)"
1924
+ oninput="wcOnExcludeChange()"
1925
+ class="flex-1 px-3 py-1.5 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 font-mono focus:outline-none focus:ring-1 focus:ring-blue-400 resize-y"
1926
+ ></textarea>
1927
+ <button
1928
+ onclick="wcToggleExcludeBrowser()"
1929
+ class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors shrink-0"
1930
+ >
1931
+ &#128193; <span data-i18n="wc.browse_btn">Browse</span>
1932
+ </button>
1933
+ </div>
1934
+ <!-- Row 1c: exclude browser (collapsed by default) -->
1935
+ <div
1936
+ id="wc-exclude-browser"
1937
+ class="hidden border-t border-gray-200 dark:border-gray-700"
1938
+ >
1939
+ <div
1940
+ 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"
1941
+ >
1942
+ <button
1943
+ id="wc-excl-browse-up"
1944
+ onclick="wcExclBrowseUp()"
1945
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
1946
+ >
1947
+ &#8593; Up
1948
+ </button>
1949
+ <span
1950
+ id="wc-excl-browse-path"
1951
+ class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
1952
+ ></span>
1953
+ </div>
1954
+ <div
1955
+ id="wc-excl-browse-list"
1956
+ class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"
1957
+ ></div>
1958
+ </div>
1959
+ <!-- Row 2: extension checkboxes -->
1960
+ <div class="flex items-center gap-x-4 gap-y-2 px-6 pb-3 flex-wrap">
1961
+ <span
1962
+ data-i18n="wc.extensions_label"
1963
+ class="text-xs font-medium text-gray-500 dark:text-gray-400 shrink-0"
1964
+ >Extensions</span
1965
+ >
1966
+ <button
1967
+ onclick="wcToggleAllExts()"
1968
+ id="wcToggleAllBtn"
1969
+ data-i18n="wc.all_btn"
1970
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline shrink-0"
1971
+ >
1972
+ All
1973
+ </button>
1974
+ <label
1975
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1976
+ ><input type="checkbox" class="wc-ext" value="md" checked />
1977
+ .md</label
1978
+ >
1979
+ <label
1980
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1981
+ ><input type="checkbox" class="wc-ext" value="mdx" /> .mdx</label
1982
+ >
1983
+ <label
1984
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1985
+ ><input type="checkbox" class="wc-ext" value="txt" /> .txt</label
1986
+ >
1987
+ <span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
1988
+ <label
1989
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1990
+ ><input type="checkbox" class="wc-ext" value="ts" /> .ts</label
1991
+ >
1992
+ <label
1993
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1994
+ ><input type="checkbox" class="wc-ext" value="tsx" /> .tsx</label
1995
+ >
1996
+ <label
1997
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
1998
+ ><input type="checkbox" class="wc-ext" value="js" /> .js</label
1999
+ >
2000
+ <label
2001
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2002
+ ><input type="checkbox" class="wc-ext" value="jsx" /> .jsx</label
2003
+ >
2004
+ <label
2005
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2006
+ ><input type="checkbox" class="wc-ext" value="mjs" /> .mjs</label
2007
+ >
2008
+ <span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
2009
+ <label
2010
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2011
+ ><input type="checkbox" class="wc-ext" value="java" /> .java</label
2012
+ >
2013
+ <label
2014
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2015
+ ><input type="checkbox" class="wc-ext" value="kt" /> .kt</label
2016
+ >
2017
+ <label
2018
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2019
+ ><input type="checkbox" class="wc-ext" value="py" /> .py</label
2020
+ >
2021
+ <label
2022
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2023
+ ><input type="checkbox" class="wc-ext" value="go" /> .go</label
2024
+ >
2025
+ <label
2026
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2027
+ ><input type="checkbox" class="wc-ext" value="rs" /> .rs</label
2028
+ >
2029
+ <label
2030
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2031
+ ><input type="checkbox" class="wc-ext" value="cs" /> .cs</label
2032
+ >
2033
+ <label
2034
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2035
+ ><input type="checkbox" class="wc-ext" value="swift" />
2036
+ .swift</label
2037
+ >
2038
+ <label
2039
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2040
+ ><input type="checkbox" class="wc-ext" value="rb" /> .rb</label
2041
+ >
2042
+ <span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
2043
+ <label
2044
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2045
+ ><input type="checkbox" class="wc-ext" value="prisma" />
2046
+ .prisma</label
2047
+ >
2048
+ <label
2049
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2050
+ ><input type="checkbox" class="wc-ext" value="graphql" />
2051
+ .graphql</label
2052
+ >
2053
+ <label
2054
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2055
+ ><input type="checkbox" class="wc-ext" value="gql" /> .gql</label
2056
+ >
2057
+ <span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
2058
+ <label
2059
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2060
+ ><input type="checkbox" class="wc-ext" value="html" /> .html</label
2061
+ >
2062
+ <label
2063
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2064
+ ><input type="checkbox" class="wc-ext" value="css" /> .css</label
2065
+ >
2066
+ <label
2067
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2068
+ ><input type="checkbox" class="wc-ext" value="scss" /> .scss</label
2069
+ >
2070
+ <span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
2071
+ <label
2072
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2073
+ ><input type="checkbox" class="wc-ext" value="yml" /> .yml</label
2074
+ >
2075
+ <label
2076
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2077
+ ><input type="checkbox" class="wc-ext" value="yaml" /> .yaml</label
2078
+ >
2079
+ <label
2080
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2081
+ ><input type="checkbox" class="wc-ext" value="json" /> .json</label
2082
+ >
2083
+ <label
2084
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2085
+ ><input type="checkbox" class="wc-ext" value="xml" /> .xml</label
2086
+ >
2087
+ <label
2088
+ class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"
2089
+ ><input type="checkbox" class="wc-ext" value="toml" /> .toml</label
2090
+ >
2091
+ </div>
2092
+ <!-- Row 3: inline folder browser (collapsed by default) -->
2093
+ <div
2094
+ id="wc-browser"
2095
+ class="hidden border-t border-gray-200 dark:border-gray-700"
2096
+ >
2097
+ <div
2098
+ 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"
2099
+ >
2100
+ <button
2101
+ id="wc-browse-up"
2102
+ onclick="wcBrowseUp()"
2103
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0"
2104
+ >
2105
+ &#8593; Up
2106
+ </button>
2107
+ <span
2108
+ id="wc-browse-path"
2109
+ class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"
2110
+ ></span>
2111
+ </div>
2112
+ <div
2113
+ id="wc-browse-list"
2114
+ class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"
2115
+ ></div>
2116
+ </div>
2117
+ </div>
2118
+ <div id="wc-body" class="flex-1 overflow-hidden flex">
2119
+ <!-- Left sidebar: stats + controls + top-50 words -->
2120
+ <div
2121
+ id="wc-sidebar"
2122
+ class="hidden w-56 shrink-0 border-r border-gray-200 dark:border-gray-800 flex flex-col bg-white dark:bg-gray-900 overflow-hidden"
2123
+ >
2124
+ <div
2125
+ id="wc-stats"
2126
+ class="px-3 py-2 text-xs text-gray-500 dark:text-gray-400 space-y-0.5 border-b border-gray-100 dark:border-gray-800"
2127
+ ></div>
2128
+ <div
2129
+ class="px-3 py-2 border-b border-gray-100 dark:border-gray-800 flex items-center gap-2"
2130
+ >
2131
+ <span data-i18n="wc.min_files_label" class="text-xs text-gray-500 dark:text-gray-400 shrink-0"
2132
+ >Min files</span
2133
+ >
2134
+ <input
2135
+ id="wc-min-files"
2136
+ type="number"
2137
+ min="1"
2138
+ max="20"
2139
+ value="1"
2140
+ class="w-12 px-1.5 py-0.5 text-xs rounded border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-800 dark:text-gray-200 focus:outline-none"
2141
+ />
2142
+ <button
2143
+ onclick="wcApplyFilter()"
2144
+ data-i18n="wc.apply_btn"
2145
+ class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
2146
+ >
2147
+ Apply
2148
+ </button>
2149
+ </div>
2150
+ <p
2151
+ data-i18n="wc.top_words"
2152
+ class="px-3 pt-2 pb-1 text-xs font-semibold text-gray-400 dark:text-gray-600 uppercase tracking-wider"
2153
+ >
2154
+ Top words
2155
+ </p>
2156
+ <div id="wc-top-list" class="flex-1 overflow-y-auto"></div>
2157
+ </div>
2158
+ <!-- Canvas / status area -->
2159
+ <div id="wc-canvas-wrap" class="flex-1 relative overflow-hidden">
2160
+ <p
2161
+ id="wc-status"
2162
+ class="absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500 text-sm animate-pulse"
2163
+ ></p>
2164
+ <canvas
2165
+ id="wc-canvas"
2166
+ class="hidden"
2167
+ style="position: absolute; inset: 0"
2168
+ ></canvas>
2169
+ </div>
2170
+ <!-- Right detail panel: shown on word click -->
2171
+ <div
2172
+ id="wc-detail"
2173
+ class="hidden w-80 shrink-0 border-l border-gray-200 dark:border-gray-800 flex flex-col bg-white dark:bg-gray-900 overflow-hidden"
2174
+ >
2175
+ <div
2176
+ class="px-3 py-2 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between"
2177
+ >
2178
+ <span
2179
+ id="wc-detail-word"
2180
+ class="font-bold text-sm text-gray-900 dark:text-gray-100"
2181
+ ></span>
2182
+ <button
2183
+ onclick="wcCloseDetail()"
2184
+ class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 text-xs leading-none"
2185
+ >
2186
+
2187
+ </button>
2188
+ </div>
2189
+ <p
2190
+ data-i18n="wc.found_in"
2191
+ class="px-3 pt-2 pb-1 text-xs font-semibold text-gray-400 dark:text-gray-600 uppercase tracking-wider"
2192
+ >
2193
+ Found in
2194
+ </p>
2195
+ <div
2196
+ id="wc-detail-files"
2197
+ class="flex-1 overflow-y-auto px-3 py-1 space-y-1 text-xs"
2198
+ ></div>
2199
+ </div>
2200
+ </div>
2201
+ </div>
2202
+ <!-- All inline logic extracted to modules under src/frontend/*.js -->
2203
+
2204
+ <!-- ── Export modal (Markdown / HTML / PDF) ────────────────────────────── -->
2205
+ <div
2206
+ id="export-modal"
2207
+ class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
2208
+ >
2209
+ <div class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-full max-w-sm mx-4 p-6 space-y-4">
2210
+ <!-- Title -->
2211
+ <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
2212
+ <i class="fa-solid fa-file-export mr-2 text-blue-500"></i>
2213
+ <span data-i18n="modal.export.title">Export</span>
2214
+ </h3>
2215
+
2216
+ <!-- Tabs -->
2217
+ <div class="flex gap-0 border-b border-gray-200 dark:border-gray-700">
2218
+ <button
2219
+ id="export-tab-markdown"
2220
+ onclick="switchExportTab('markdown')"
2221
+ data-i18n="modal.export.tab_markdown"
2222
+ class="px-4 py-2 text-sm border-b-2 border-blue-500 text-blue-600 dark:text-blue-400 font-semibold transition-colors"
2223
+ >Markdown</button>
2224
+ <button
2225
+ id="export-tab-html"
2226
+ onclick="switchExportTab('html')"
2227
+ data-i18n="modal.export.tab_html"
2228
+ class="px-4 py-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
2229
+ >HTML</button>
2230
+ <button
2231
+ id="export-tab-pdf"
2232
+ onclick="switchExportTab('pdf')"
2233
+ data-i18n="modal.export.tab_pdf"
2234
+ class="px-4 py-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors"
2235
+ >PDF</button>
2236
+ </div>
2237
+
2238
+ <!-- Folder list (html tab only) -->
2239
+ <div id="export-folder-section" class="hidden">
2240
+ <div
2241
+ id="export-folder-list"
2242
+ class="max-h-48 overflow-y-auto space-y-0.5 border border-gray-200 dark:border-gray-700 rounded-lg p-2"
2243
+ ></div>
2244
+ </div>
2245
+
2246
+ <!-- Markdown tab content -->
2247
+ <div id="export-content-markdown">
2248
+ <p class="text-xs text-gray-500 dark:text-gray-400" data-i18n="modal.export.markdown_hint">
2249
+ Export all documents and images as Markdown files, with internal links rewritten to relative paths.
2250
+ </p>
2251
+ <div class="flex justify-end gap-3 pt-2">
2252
+ <button
2253
+ onclick="closeExportModal()"
2254
+ data-i18n="common.cancel"
2255
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
2256
+ >Cancel</button>
2257
+ <button
2258
+ id="export-md-btn"
2259
+ onclick="exportMarkdown()"
2260
+ data-i18n="modal.export.markdown_btn"
2261
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
2262
+ >Export Markdown</button>
2263
+ </div>
2264
+ </div>
2265
+
2266
+ <!-- HTML tab content -->
2267
+ <div id="export-content-html" class="hidden">
2268
+ <p class="text-xs text-gray-500 dark:text-gray-400" data-i18n="modal.export_html.hint">
2269
+ Select the folders to include in the ZIP archive.
2270
+ </p>
2271
+ <div class="flex justify-end gap-3 pt-2">
2272
+ <button
2273
+ onclick="closeExportModal()"
2274
+ data-i18n="common.cancel"
2275
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
2276
+ >Cancel</button>
2277
+ <button
2278
+ id="export-notion-btn"
2279
+ onclick="exportHtml('notion')"
2280
+ data-i18n="modal.export_html.notion_btn"
2281
+ class="text-sm px-4 py-2 rounded-lg border border-blue-500 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/20 font-semibold transition-colors"
2282
+ >Export Notion</button>
2283
+ <button
2284
+ id="export-confluence-btn"
2285
+ onclick="exportHtml('confluence')"
2286
+ data-i18n="modal.export_html.confluence_btn"
2287
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
2288
+ >Export Confluence</button>
2289
+ </div>
2290
+ </div>
2291
+
2292
+ <!-- PDF tab content -->
2293
+ <div id="export-content-pdf" class="hidden">
2294
+ <p class="text-xs text-gray-500 dark:text-gray-400" data-i18n="modal.export.pdf_hint">
2295
+ Export all documents as a single PDF with table of contents.
2296
+ </p>
2297
+ <div class="flex justify-end gap-3 pt-2">
2298
+ <button
2299
+ onclick="closeExportModal()"
2300
+ data-i18n="common.cancel"
2301
+ class="text-sm px-4 py-2 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
2302
+ >Cancel</button>
2303
+ <button
2304
+ id="export-pdf-btn"
2305
+ onclick="exportAllPdfFromModal()"
2306
+ data-i18n="modal.export.pdf_btn"
2307
+ class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
2308
+ >Export all PDF</button>
2309
+ </div>
2310
+ </div>
2311
+ </div>
2312
+ </div>
2313
+ </body>
2314
+ </html>