living-ai-documentation 1.11.0 → 2.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 (44) hide show
  1. package/dist/src/frontend/boot.js +1 -0
  2. package/dist/src/frontend/context.html +1 -1
  3. package/dist/src/frontend/documents.js +46 -0
  4. package/dist/src/frontend/i18n/en.json +35 -0
  5. package/dist/src/frontend/i18n/fr.json +35 -0
  6. package/dist/src/frontend/index.html +221 -10
  7. package/dist/src/frontend/sidebar-helpers.js +33 -0
  8. package/dist/src/frontend/sidebar.js +28 -0
  9. package/dist/src/frontend/{inline-snippet-edit.js → snippets/inline-snippet-edit.js} +112 -32
  10. package/dist/src/frontend/snippets/snippet-builders.js +138 -0
  11. package/dist/src/frontend/{snippet-detect.js → snippets/snippet-detect.js} +3 -3
  12. package/dist/src/frontend/snippets/snippet-list-markdown.js +92 -0
  13. package/dist/src/frontend/snippets/snippet-parsers.js +156 -0
  14. package/dist/src/frontend/snippets/snippet-table-attributes.js +126 -0
  15. package/dist/src/frontend/{snippet-table.js → snippets/snippet-table.js} +6 -0
  16. package/dist/src/frontend/{snippets.js → snippets/snippets.js} +300 -341
  17. package/dist/src/frontend/state.js +11 -0
  18. package/dist/src/frontend/workspace/README.md +65 -0
  19. package/dist/src/frontend/workspace/app.js +1234 -0
  20. package/dist/src/frontend/workspace/app.js.map +1 -0
  21. package/dist/src/frontend/workspace/app.ts +1622 -0
  22. package/dist/src/frontend/workspace/index.html +247 -0
  23. package/dist/src/frontend/workspace/persistence.js +37 -0
  24. package/dist/src/frontend/workspace/persistence.js.map +1 -0
  25. package/dist/src/frontend/workspace/persistence.ts +49 -0
  26. package/dist/src/frontend/workspace/styles.css +568 -0
  27. package/dist/src/frontend/workspace/tsconfig.json +12 -0
  28. package/dist/src/routes/documents.d.ts.map +1 -1
  29. package/dist/src/routes/documents.js +23 -0
  30. package/dist/src/routes/documents.js.map +1 -1
  31. package/dist/src/routes/workspace.d.ts +3 -0
  32. package/dist/src/routes/workspace.d.ts.map +1 -0
  33. package/dist/src/routes/workspace.js +154 -0
  34. package/dist/src/routes/workspace.js.map +1 -0
  35. package/dist/src/server.d.ts.map +1 -1
  36. package/dist/src/server.js +3 -0
  37. package/dist/src/server.js.map +1 -1
  38. package/package.json +3 -2
  39. /package/dist/src/frontend/{confirm-modal.js → modals/confirm-modal.js} +0 -0
  40. /package/dist/src/frontend/{diagram-link-modal.js → modals/diagram-link-modal.js} +0 -0
  41. /package/dist/src/frontend/{files-modal.js → modals/files-modal.js} +0 -0
  42. /package/dist/src/frontend/{new-doc-modal.js → modals/new-doc-modal.js} +0 -0
  43. /package/dist/src/frontend/{new-folder-modal.js → modals/new-folder-modal.js} +0 -0
  44. /package/dist/src/frontend/{snippet-tree.js → snippets/snippet-tree.js} +0 -0
@@ -19,6 +19,7 @@ document.addEventListener("DOMContentLoaded", async () => {
19
19
  await loadDocuments();
20
20
  applyHideCategoriesButtonState();
21
21
  applyHideAttachmentsButtonState();
22
+ applyHighlightStatusButtonState();
22
23
 
23
24
  // Deep-link via ?doc=id, otherwise open first General doc
24
25
  const params = new URLSearchParams(location.search);
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>AI Context</title>
7
7
  <script src="/i18n.js"></script>
8
- <script src="/confirm-modal.js"></script>
8
+ <script src="/modals/confirm-modal.js"></script>
9
9
  <script src="https://cdn.tailwindcss.com?plugins=typography"></script>
10
10
  <script>
11
11
  tailwind.config = { darkMode: "class", theme: { extend: {} } };
@@ -8,10 +8,43 @@
8
8
  let _lastDocHtml = null;
9
9
  let _lastDocIdRendered = null;
10
10
 
11
+ function _applyTableStyles(contentEl) {
12
+ const attrs = ldCollectTableAttributesFromSource(
13
+ typeof currentDocContent === "string" ? currentDocContent : "",
14
+ );
15
+ const tables = contentEl.querySelectorAll("table");
16
+ tables.forEach((table, i) => {
17
+ const a = attrs[i];
18
+ if (!a) return;
19
+ if (a.style) {
20
+ table.classList.add(`table-style-${a.style}`);
21
+ table.dataset.tableStyle = a.style;
22
+ }
23
+ if (a.border) {
24
+ table.classList.add(`table-border-${a.border}`);
25
+ table.dataset.tableBorder = a.border;
26
+ }
27
+ if (a.color) {
28
+ table.classList.add(`table-color-${a.color}`);
29
+ table.dataset.tableColor = a.color;
30
+ }
31
+ });
32
+ }
33
+
34
+ function _fillEmptyTableCells(contentEl) {
35
+ contentEl.querySelectorAll("table th, table td").forEach((cell) => {
36
+ if (cell.textContent.trim() === "") {
37
+ cell.textContent = "\u00A0";
38
+ }
39
+ });
40
+ }
41
+
11
42
  function _wireDocContent(html) {
12
43
  const contentEl = document.getElementById("doc-content");
13
44
  if (!contentEl) return;
14
45
  contentEl.innerHTML = html;
46
+ _applyTableStyles(contentEl);
47
+ _fillEmptyTableCells(contentEl);
15
48
 
16
49
  contentEl.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((h) => {
17
50
  if (!h.id) {
@@ -187,6 +220,7 @@ async function loadDocuments() {
187
220
  await Promise.all([
188
221
  refreshAnnotationCounts(),
189
222
  refreshFileAttachmentCounts(),
223
+ refreshDocStatuses(),
190
224
  ]);
191
225
  renderSidebar(allDocs);
192
226
  } catch {
@@ -222,6 +256,18 @@ async function refreshFileAttachmentCounts() {
222
256
  }
223
257
  }
224
258
 
259
+ async function refreshDocStatuses() {
260
+ try {
261
+ const raw = await fetch("/api/documents/statuses").then((r) => r.json());
262
+ docStatuses = {};
263
+ for (const [docId, status] of Object.entries(raw || {})) {
264
+ docStatuses[docId] = status;
265
+ }
266
+ } catch {
267
+ docStatuses = {};
268
+ }
269
+ }
270
+
225
271
  async function openDocument(id, skipHistory = false, fromLink = false, anchor = null) {
226
272
  // Track navigation history for breadcrumb trail
227
273
  // fromLink===true : forward navigation via in-doc link → push current to stack
@@ -19,10 +19,12 @@
19
19
  "nav.toggle_sidebar": "Toggle sidebar",
20
20
  "nav.search_placeholder": "Search docs…",
21
21
  "nav.search_mobile_placeholder": "Search…",
22
+ "nav.workspace": "Workspace",
22
23
  "nav.toggle_dark": "Toggle dark mode",
23
24
  "nav.export": "Export",
24
25
  "nav.toggle_categories": "Show/hide category grouping",
25
26
  "nav.toggle_attachments": "Show/hide attachments",
27
+ "nav.toggle_status_highlight": "Highlight document statuses (To be validated / SuperSeeded)",
26
28
  "nav.export_html": "Export folders as HTML ZIP",
27
29
  "nav.export_all_pdf": "Export all documents as PDF",
28
30
  "nav.new_folder": "New folder",
@@ -65,6 +67,8 @@
65
67
  "sidebar.annotated_docs_badge": "document with annotations",
66
68
  "sidebar.file_attachment_badge": "attachment",
67
69
  "sidebar.file_attached_docs_badge": "document with attachments",
70
+ "sidebar.status_to_validate": "To be validated",
71
+ "sidebar.status_superseeded": "SuperSeeded",
68
72
 
69
73
  "doc.validate_mode": "Validate document — flip frontmatter status from \"To be validated\" to \"Accepted\"",
70
74
  "doc.validate_btn": "Validate",
@@ -281,6 +285,37 @@
281
285
  "snippet.inline_edit_btn_heading_2": "Edit heading level 2",
282
286
  "snippet.inline_edit_btn_heading_3": "Edit heading level 3",
283
287
  "snippet.inline_edit_btn_heading_4": "Edit heading level 4",
288
+ "snippet.inline_delete_btn_table": "Delete table",
289
+ "snippet.inline_delete_btn_code_block": "Delete code block",
290
+ "snippet.inline_delete_btn_blockquote": "Delete blockquote",
291
+ "snippet.inline_delete_btn_ordered_list": "Delete numbered list",
292
+ "snippet.inline_delete_btn_unordered_list": "Delete bullet list",
293
+ "snippet.inline_delete_btn_tree": "Delete tree",
294
+ "snippet.inline_delete_btn_colored_section": "Delete colored section",
295
+ "snippet.inline_delete_btn_colored_text": "Delete colored text",
296
+ "snippet.inline_delete_btn_collapsible": "Delete collapsible block",
297
+ "snippet.inline_delete_btn_link": "Delete link",
298
+ "snippet.inline_delete_btn_doc_link": "Delete document link",
299
+ "snippet.inline_delete_btn_anchor_link": "Delete anchor link",
300
+ "snippet.inline_delete_btn_anchor_doc_link": "Delete document + anchor link",
301
+ "snippet.inline_delete_btn_image": "Delete image",
302
+ "snippet.inline_delete_btn_separator": "Delete separator",
303
+ "snippet.inline_delete_btn_heading_1": "Delete heading level 1",
304
+ "snippet.inline_delete_btn_heading_2": "Delete heading level 2",
305
+ "snippet.inline_delete_btn_heading_3": "Delete heading level 3",
306
+ "snippet.inline_delete_btn_heading_4": "Delete heading level 4",
307
+ "snippet.table_style_label": "Table style",
308
+ "snippet.table_style_default": "Default",
309
+ "snippet.table_style_compact": "Compact",
310
+ "snippet.table_style_striped": "Striped",
311
+ "snippet.table_border_label": "Borders",
312
+ "snippet.table_color_label": "Color",
313
+ "snippet.table_color_default": "None",
314
+ "snippet.table_color_info": "Info (blue)",
315
+ "snippet.table_color_success": "Success (green)",
316
+ "snippet.table_color_warning": "Warning (orange)",
317
+ "snippet.table_color_danger": "Danger (red)",
318
+ "snippet.table_color_note": "Note (violet)",
284
319
  "snippet.picker_search_placeholder": "Search a snippet…",
285
320
  "snippet.picker_back": "Back",
286
321
  "snippet.picker_no_results": "No matching snippet.",
@@ -19,10 +19,12 @@
19
19
  "nav.toggle_sidebar": "Afficher/masquer le panneau",
20
20
  "nav.search_placeholder": "Rechercher dans les docs…",
21
21
  "nav.search_mobile_placeholder": "Rechercher…",
22
+ "nav.workspace": "Workspace",
22
23
  "nav.toggle_dark": "Basculer le mode sombre",
23
24
  "nav.export": "Exporter",
24
25
  "nav.toggle_categories": "Afficher/masquer le regroupement par catégorie",
25
26
  "nav.toggle_attachments": "Afficher/masquer les pièces jointes",
27
+ "nav.toggle_status_highlight": "Mettre en évidence les statuts (À valider / Obsolète)",
26
28
  "nav.export_html": "Exporter les dossiers en HTML (ZIP)",
27
29
  "nav.export_all_pdf": "Exporter tous les documents en PDF",
28
30
  "nav.new_folder": "Nouveau dossier",
@@ -65,6 +67,8 @@
65
67
  "sidebar.annotated_docs_badge": "document contenant des annotations",
66
68
  "sidebar.file_attachment_badge": "pièce jointe",
67
69
  "sidebar.file_attached_docs_badge": "document contenant des pièces jointes",
70
+ "sidebar.status_to_validate": "À valider",
71
+ "sidebar.status_superseeded": "Obsolète",
68
72
 
69
73
  "doc.validate_mode": "Valider le document — passer le statut frontmatter de « To be validated » à « Accepted »",
70
74
  "doc.validate_btn": "Valider",
@@ -281,6 +285,37 @@
281
285
  "snippet.inline_edit_btn_heading_2": "Éditer le titre niveau 2",
282
286
  "snippet.inline_edit_btn_heading_3": "Éditer le titre niveau 3",
283
287
  "snippet.inline_edit_btn_heading_4": "Éditer le titre niveau 4",
288
+ "snippet.inline_delete_btn_table": "Supprimer le tableau",
289
+ "snippet.inline_delete_btn_code_block": "Supprimer le bloc de code",
290
+ "snippet.inline_delete_btn_blockquote": "Supprimer la citation",
291
+ "snippet.inline_delete_btn_ordered_list": "Supprimer la liste numérotée",
292
+ "snippet.inline_delete_btn_unordered_list": "Supprimer la liste à puces",
293
+ "snippet.inline_delete_btn_tree": "Supprimer l'arborescence",
294
+ "snippet.inline_delete_btn_colored_section": "Supprimer la section colorée",
295
+ "snippet.inline_delete_btn_colored_text": "Supprimer le texte coloré",
296
+ "snippet.inline_delete_btn_collapsible": "Supprimer le bloc repliable",
297
+ "snippet.inline_delete_btn_link": "Supprimer le lien",
298
+ "snippet.inline_delete_btn_doc_link": "Supprimer le lien vers un document",
299
+ "snippet.inline_delete_btn_anchor_link": "Supprimer le lien d'ancre",
300
+ "snippet.inline_delete_btn_anchor_doc_link": "Supprimer le lien document + ancre",
301
+ "snippet.inline_delete_btn_image": "Supprimer l'image",
302
+ "snippet.inline_delete_btn_separator": "Supprimer le séparateur",
303
+ "snippet.inline_delete_btn_heading_1": "Supprimer le titre niveau 1",
304
+ "snippet.inline_delete_btn_heading_2": "Supprimer le titre niveau 2",
305
+ "snippet.inline_delete_btn_heading_3": "Supprimer le titre niveau 3",
306
+ "snippet.inline_delete_btn_heading_4": "Supprimer le titre niveau 4",
307
+ "snippet.table_style_label": "Style du tableau",
308
+ "snippet.table_style_default": "Par défaut",
309
+ "snippet.table_style_compact": "Compact",
310
+ "snippet.table_style_striped": "Rayé",
311
+ "snippet.table_border_label": "Bordures",
312
+ "snippet.table_color_label": "Couleur",
313
+ "snippet.table_color_default": "Aucune",
314
+ "snippet.table_color_info": "Info (bleu)",
315
+ "snippet.table_color_success": "Succès (vert)",
316
+ "snippet.table_color_warning": "Avertissement (orange)",
317
+ "snippet.table_color_danger": "Danger (rouge)",
318
+ "snippet.table_color_note": "Note (violet)",
284
319
  "snippet.picker_search_placeholder": "Rechercher un snippet…",
285
320
  "snippet.picker_back": "Retour",
286
321
  "snippet.picker_no_results": "Aucun snippet ne correspond.",
@@ -34,9 +34,13 @@
34
34
  <script defer src="/utils.js"></script>
35
35
  <script defer src="/state.js"></script>
36
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>
37
+ <script defer src="/snippets/snippet-table-attributes.js"></script>
38
+ <script defer src="/snippets/snippet-list-markdown.js"></script>
39
+ <script defer src="/snippets/snippet-builders.js"></script>
40
+ <script defer src="/snippets/snippet-parsers.js"></script>
41
+ <script defer src="/snippets/snippet-detect.js"></script>
42
+ <script defer src="/snippets/snippet-table.js"></script>
43
+ <script defer src="/snippets/snippet-tree.js"></script>
40
44
  <script defer src="/dark-mode.js"></script>
41
45
  <script defer src="/config.js"></script>
42
46
  <script defer src="/sidebar.js"></script>
@@ -47,18 +51,18 @@
47
51
  <script defer src="/file-attach.js"></script>
48
52
  <script defer src="/documents.js"></script>
49
53
  <script defer src="/misc.js"></script>
50
- <script defer src="/snippets.js"></script>
51
- <script defer src="/inline-snippet-edit.js"></script>
54
+ <script defer src="/snippets/snippets.js"></script>
55
+ <script defer src="/snippets/inline-snippet-edit.js"></script>
52
56
  <script defer src="/annotations.js"></script>
53
57
  <script defer src="/metadata.js"></script>
54
58
  <script defer src="/accuracy-gauge.js"></script>
55
59
  <script defer src="/validate.js"></script>
56
60
  <script defer src="/export.js"></script>
57
- <script defer src="/diagram-link-modal.js"></script>
58
- <script defer src="/new-folder-modal.js"></script>
59
- <script defer src="/new-doc-modal.js"></script>
60
- <script defer src="/confirm-modal.js"></script>
61
- <script defer src="/files-modal.js"></script>
61
+ <script defer src="/modals/diagram-link-modal.js"></script>
62
+ <script defer src="/modals/new-folder-modal.js"></script>
63
+ <script defer src="/modals/new-doc-modal.js"></script>
64
+ <script defer src="/modals/confirm-modal.js"></script>
65
+ <script defer src="/modals/files-modal.js"></script>
62
66
  <script defer src="/boot.js"></script>
63
67
 
64
68
  <script>
@@ -128,6 +132,149 @@
128
132
  border-color: rgba(34, 197, 94, 0.4);
129
133
  }
130
134
 
135
+ /* Table presets — triggered by table-style/table-border/table-color comments. */
136
+
137
+ /* Color palette via CSS variables. Default (no color class) uses neutral fallbacks. */
138
+ table.table-color-info {
139
+ --tbl-accent: rgb(59 130 246);
140
+ --tbl-accent-soft: rgb(219 234 254);
141
+ --tbl-accent-stripe: rgb(239 246 255);
142
+ --tbl-accent-text: rgb(30 58 138);
143
+ }
144
+ table.table-color-success {
145
+ --tbl-accent: rgb(34 197 94);
146
+ --tbl-accent-soft: rgb(220 252 231);
147
+ --tbl-accent-stripe: rgb(240 253 244);
148
+ --tbl-accent-text: rgb(20 83 45);
149
+ }
150
+ table.table-color-warning {
151
+ --tbl-accent: rgb(245 158 11);
152
+ --tbl-accent-soft: rgb(254 243 199);
153
+ --tbl-accent-stripe: rgb(255 251 235);
154
+ --tbl-accent-text: rgb(120 53 15);
155
+ }
156
+ table.table-color-danger {
157
+ --tbl-accent: rgb(239 68 68);
158
+ --tbl-accent-soft: rgb(254 226 226);
159
+ --tbl-accent-stripe: rgb(254 242 242);
160
+ --tbl-accent-text: rgb(127 29 29);
161
+ }
162
+ table.table-color-note {
163
+ --tbl-accent: rgb(139 92 246);
164
+ --tbl-accent-soft: rgb(233 213 255);
165
+ --tbl-accent-stripe: rgb(245 243 255);
166
+ --tbl-accent-text: rgb(76 29 149);
167
+ }
168
+ .dark table.table-color-info {
169
+ --tbl-accent: rgb(96 165 250);
170
+ --tbl-accent-soft: rgb(30 58 138 / 0.55);
171
+ --tbl-accent-stripe: rgb(30 58 138 / 0.35);
172
+ --tbl-accent-text: rgb(191 219 254);
173
+ }
174
+ .dark table.table-color-success {
175
+ --tbl-accent: rgb(74 222 128);
176
+ --tbl-accent-soft: rgb(20 83 45 / 0.55);
177
+ --tbl-accent-stripe: rgb(20 83 45 / 0.35);
178
+ --tbl-accent-text: rgb(187 247 208);
179
+ }
180
+ .dark table.table-color-warning {
181
+ --tbl-accent: rgb(251 191 36);
182
+ --tbl-accent-soft: rgb(120 53 15 / 0.55);
183
+ --tbl-accent-stripe: rgb(120 53 15 / 0.35);
184
+ --tbl-accent-text: rgb(254 215 170);
185
+ }
186
+ .dark table.table-color-danger {
187
+ --tbl-accent: rgb(248 113 113);
188
+ --tbl-accent-soft: rgb(127 29 29 / 0.55);
189
+ --tbl-accent-stripe: rgb(127 29 29 / 0.35);
190
+ --tbl-accent-text: rgb(254 202 202);
191
+ }
192
+ .dark table.table-color-note {
193
+ --tbl-accent: rgb(167 139 250);
194
+ --tbl-accent-soft: rgb(76 29 149 / 0.55);
195
+ --tbl-accent-stripe: rgb(76 29 149 / 0.35);
196
+ --tbl-accent-text: rgb(221 214 254);
197
+ }
198
+ /* When a color is applied, tint the header with the accent palette. */
199
+ .prose table[class*="table-color-"] thead th {
200
+ background-color: var(--tbl-accent-soft);
201
+ color: var(--tbl-accent-text);
202
+ }
203
+
204
+ /* Default table type: generous padding with horizontal separators only. */
205
+ .prose table {
206
+ border-collapse: collapse;
207
+ border: none;
208
+ }
209
+ .prose table th,
210
+ .prose table td {
211
+ padding: 0.6rem 1rem;
212
+ border: none;
213
+ border-bottom: 1px solid rgb(229 231 235);
214
+ }
215
+ .prose table thead th {
216
+ border-bottom-color: rgb(209 213 219);
217
+ }
218
+ .dark .prose table th,
219
+ .dark .prose table td {
220
+ border-bottom-color: rgb(55 65 81);
221
+ }
222
+ .prose table[class*="table-color-"] th,
223
+ .prose table[class*="table-color-"] td {
224
+ border-bottom-color: var(--tbl-accent-soft);
225
+ }
226
+ .prose table[class*="table-color-"] thead th {
227
+ border-bottom-color: var(--tbl-accent);
228
+ }
229
+ .prose table thead,
230
+ .prose table tbody tr {
231
+ border: none;
232
+ }
233
+
234
+ /* Compact: small font + reduced padding (clearly denser than other variants). */
235
+ .prose table.table-style-compact {
236
+ font-size: 0.85em;
237
+ line-height: 1.35;
238
+ }
239
+ .prose table.table-style-compact th,
240
+ .prose table.table-style-compact td {
241
+ padding: 0.2rem 0.5rem;
242
+ }
243
+
244
+ /* Striped: zebra rows using the accent stripe (neutral fallback if no color). */
245
+ .prose table.table-style-striped {
246
+ border-collapse: collapse;
247
+ }
248
+ .prose table.table-style-striped tbody tr:nth-child(odd) {
249
+ background-color: var(--tbl-accent-stripe, rgb(243 244 246));
250
+ }
251
+ .dark .prose table.table-style-striped tbody tr:nth-child(odd) {
252
+ background-color: var(--tbl-accent-stripe, rgb(55 65 81));
253
+ }
254
+
255
+ /* Border checkbox: visible borders tinted with the accent (neutral fallback otherwise). */
256
+ .prose table.table-border-bordered {
257
+ border-collapse: collapse;
258
+ }
259
+ .prose table.table-border-bordered,
260
+ .prose table.table-border-bordered th,
261
+ .prose table.table-border-bordered td {
262
+ border: 1px solid var(--tbl-accent, rgb(209 213 219));
263
+ }
264
+ .dark .prose table.table-border-bordered,
265
+ .dark .prose table.table-border-bordered th,
266
+ .dark .prose table.table-border-bordered td {
267
+ border-color: var(--tbl-accent, rgb(75 85 99));
268
+ }
269
+
270
+ /* Explicit/legacy borderless: keep only horizontal separators. */
271
+ .prose table.table-border-borderless th,
272
+ .prose table.table-border-borderless td {
273
+ border-left: none !important;
274
+ border-right: none !important;
275
+ border-top: none !important;
276
+ }
277
+
131
278
  /* Collapsible code blocks (set via --ld-code-max-h from .living-doc.json) */
132
279
  pre.ld-collapsible {
133
280
  max-height: var(--ld-code-max-h, 400px);
@@ -415,6 +562,15 @@
415
562
  >
416
563
  </div>
417
564
 
565
+ <!-- Workspace -->
566
+ <a
567
+ href="/workspace"
568
+ data-i18n="nav.workspace"
569
+ 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"
570
+ >
571
+ Workspace
572
+ </a>
573
+
418
574
  <!-- Dark mode toggle -->
419
575
  <button
420
576
  id="dark-toggle"
@@ -502,6 +658,15 @@
502
658
  class="text-xs text-gray-400 dark:text-gray-500"
503
659
  ></p>
504
660
  <div class="flex items-center gap-2">
661
+ <button
662
+ id="toggle-status-highlight-btn"
663
+ onclick="cycleHighlightStatus()"
664
+ data-i18n-title="nav.toggle_status_highlight"
665
+ title="Highlight document statuses"
666
+ class="text-gray-400 hover:opacity-80 dark:text-gray-500 transition-colors leading-none"
667
+ >
668
+ <i class="fa-solid fa-certificate"></i>
669
+ </button>
505
670
  <button
506
671
  id="toggle-attachments-btn"
507
672
  onclick="toggleHideAttachments()"
@@ -1837,6 +2002,52 @@
1837
2002
  <!-- Panel: table -->
1838
2003
  <div id="snip-panel-table" class="hidden space-y-3">
1839
2004
  <div class="flex items-center gap-5">
2005
+ <div class="flex items-center gap-2">
2006
+ <label
2007
+ for="snip-table-style"
2008
+ data-i18n="snippet.table_style_label"
2009
+ class="text-xs text-gray-500 dark:text-gray-400"
2010
+ >Table style</label
2011
+ >
2012
+ <select
2013
+ id="snip-table-style"
2014
+ onchange="snippetUpdatePreview()"
2015
+ class="text-xs rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 px-2 py-1"
2016
+ >
2017
+ <option data-i18n="snippet.table_style_default" value="">Default</option>
2018
+ <option data-i18n="snippet.table_style_compact" value="compact">Compact</option>
2019
+ <option data-i18n="snippet.table_style_striped" value="striped">Striped</option>
2020
+ </select>
2021
+ </div>
2022
+ <label class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
2023
+ <input
2024
+ id="snip-table-bordered"
2025
+ type="checkbox"
2026
+ onchange="snippetUpdatePreview()"
2027
+ class="rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500"
2028
+ />
2029
+ <span data-i18n="snippet.table_border_label">Borders</span>
2030
+ </label>
2031
+ <div class="flex items-center gap-2">
2032
+ <label
2033
+ for="snip-table-color"
2034
+ data-i18n="snippet.table_color_label"
2035
+ class="text-xs text-gray-500 dark:text-gray-400"
2036
+ >Color</label
2037
+ >
2038
+ <select
2039
+ id="snip-table-color"
2040
+ onchange="snippetUpdatePreview()"
2041
+ class="text-xs rounded border border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 px-2 py-1"
2042
+ >
2043
+ <option data-i18n="snippet.table_color_default" value="">None</option>
2044
+ <option data-i18n="snippet.table_color_info" value="info">Info (blue)</option>
2045
+ <option data-i18n="snippet.table_color_success" value="success">Success (green)</option>
2046
+ <option data-i18n="snippet.table_color_warning" value="warning">Warning (orange)</option>
2047
+ <option data-i18n="snippet.table_color_danger" value="danger">Danger (red)</option>
2048
+ <option data-i18n="snippet.table_color_note" value="note">Note (violet)</option>
2049
+ </select>
2050
+ </div>
1840
2051
  <div class="flex items-center gap-2">
1841
2052
  <span data-i18n="snippet.table_rows_label" class="text-xs text-gray-500 dark:text-gray-400"
1842
2053
  >Rows</span
@@ -82,6 +82,39 @@ function fileAttachmentBadge(count) {
82
82
  border border-sky-500 shadow-sm">${count}</span>`;
83
83
  }
84
84
 
85
+ // Lifecycle pill driven by the tri-state status-highlight toggle. A document
86
+ // carries a single frontmatter `status`, so it shows at most one pill: a green
87
+ // "V" for "To be validated" (states 1 and 2) or an orange "S" for "SuperSeeded"
88
+ // (state 2 only). Status matching is case-insensitive.
89
+ function statusPill(doc) {
90
+ if (typeof highlightStatusState === "undefined" || !highlightStatusState)
91
+ return "";
92
+ const status = String(docStatuses[doc.id] || "")
93
+ .trim()
94
+ .toLowerCase();
95
+ if (status === "to be validated") {
96
+ const label = window.t
97
+ ? window.t("sidebar.status_to_validate")
98
+ : "To be validated";
99
+ return `<span title="${esc(label)}"
100
+ class="inline-flex items-center justify-center w-5 h-5
101
+ rounded-full bg-green-500 dark:bg-green-500
102
+ text-[10px] font-bold text-white
103
+ border border-green-600 shadow-sm">V</span>`;
104
+ }
105
+ if (highlightStatusState === 2 && status === "superseeded") {
106
+ const label = window.t
107
+ ? window.t("sidebar.status_superseeded")
108
+ : "SuperSeeded";
109
+ return `<span title="${esc(label)}"
110
+ class="inline-flex items-center justify-center w-5 h-5
111
+ rounded-full bg-orange-500 dark:bg-orange-500
112
+ text-[10px] font-bold text-white
113
+ border border-orange-600 shadow-sm">S</span>`;
114
+ }
115
+ return "";
116
+ }
117
+
85
118
  function fileAttachedDocsBadge(count) {
86
119
  if (!count) return "";
87
120
  if (typeof hideAttachments !== "undefined" && hideAttachments) return "";
@@ -194,6 +194,7 @@ function renderDocItem(doc) {
194
194
  <div class="leading-snug flex items-center justify-between gap-2">
195
195
  <span class="truncate">${esc(doc.title)}</span>
196
196
  <span class="flex items-center gap-1 shrink-0">
197
+ ${statusPill(doc)}
197
198
  ${annotationBadge(annCount)}
198
199
  ${fileAttachmentBadge(fileCount)}
199
200
  </span>
@@ -349,3 +350,30 @@ function applyHideAttachmentsButtonState() {
349
350
  btn.classList.toggle("text-gray-400", hideAttachments);
350
351
  btn.classList.toggle("dark:text-gray-500", hideAttachments);
351
352
  }
353
+
354
+ function cycleHighlightStatus() {
355
+ highlightStatusState = (highlightStatusState + 1) % 3;
356
+ try {
357
+ localStorage.setItem("ld-highlight-status", String(highlightStatusState));
358
+ } catch {
359
+ /* ignore */
360
+ }
361
+ applyHighlightStatusButtonState();
362
+ refreshSidebar();
363
+ }
364
+
365
+ // Icon colour mirrors the active state: gray (off) → green (To be validated) →
366
+ // orange (also SuperSeeded). Exactly one colour class is on at a time.
367
+ function applyHighlightStatusButtonState() {
368
+ const btn = document.getElementById("toggle-status-highlight-btn");
369
+ if (!btn) return;
370
+ const off = highlightStatusState === 0;
371
+ const validate = highlightStatusState === 1;
372
+ const both = highlightStatusState === 2;
373
+ btn.classList.toggle("text-gray-400", off);
374
+ btn.classList.toggle("dark:text-gray-500", off);
375
+ btn.classList.toggle("text-green-500", validate);
376
+ btn.classList.toggle("dark:text-green-400", validate);
377
+ btn.classList.toggle("text-orange-500", both);
378
+ btn.classList.toggle("dark:text-orange-400", both);
379
+ }