living-ai-documentation 1.4.0 → 1.6.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.
@@ -60,6 +60,10 @@ function _wireDocContent(html) {
60
60
  wrapper.appendChild(t);
61
61
  });
62
62
 
63
+ if (typeof initInlineSnippetEditing === "function") {
64
+ initInlineSnippetEditing(contentEl);
65
+ }
66
+
63
67
  const notice = document.getElementById("search-notice");
64
68
  const isMetaQuery =
65
69
  typeof searchQuery === "string" &&
@@ -429,6 +433,44 @@ async function confirmDeleteDocument() {
429
433
  }
430
434
 
431
435
  // ── Save (in-place edit) ─────────────────────────────────────────────────────
436
+ async function saveCurrentDocumentContent(content) {
437
+ if (!currentDocId) return;
438
+ const res = await fetch("/api/documents/" + currentDocId, {
439
+ method: "PUT",
440
+ headers: { "Content-Type": "application/json" },
441
+ body: JSON.stringify({ content }),
442
+ });
443
+ if (!res.ok) throw new Error(await res.text());
444
+
445
+ currentDocContent = content;
446
+
447
+ // Re-fetch rendered HTML and update view
448
+ const doc = await fetch("/api/documents/" + currentDocId).then((r) =>
449
+ r.json(),
450
+ );
451
+ _lastDocHtml = doc.html;
452
+ _lastDocIdRendered = currentDocId;
453
+ _wireDocContent(doc.html);
454
+
455
+ if (typeof applyAnnotationHighlights === "function") {
456
+ applyAnnotationHighlights();
457
+ }
458
+ if (typeof renderElevator === "function") renderElevator();
459
+
460
+ const fileLinkMatches = content.match(/\]\(\s*\.?\/files\/[^)\s]+/g);
461
+ const fileLinkCount = fileLinkMatches ? fileLinkMatches.length : 0;
462
+ if (fileLinkCount > 0) fileAttachmentCounts[currentDocId] = fileLinkCount;
463
+ else delete fileAttachmentCounts[currentDocId];
464
+ refreshSidebar();
465
+
466
+ if (typeof updateValidateButtonForCurrentDoc === "function") {
467
+ updateValidateButtonForCurrentDoc();
468
+ }
469
+ if (typeof loadMetadataReport === "function") {
470
+ loadMetadataReport(currentDocId);
471
+ }
472
+ }
473
+
432
474
  async function saveDocument() {
433
475
  if (!currentDocId) return;
434
476
  const content = document.getElementById("doc-editor").value;
@@ -437,31 +479,7 @@ async function saveDocument() {
437
479
  msgEl.className = "text-xs text-gray-400";
438
480
 
439
481
  try {
440
- const res = await fetch("/api/documents/" + currentDocId, {
441
- method: "PUT",
442
- headers: { "Content-Type": "application/json" },
443
- body: JSON.stringify({ content }),
444
- });
445
- if (!res.ok) throw new Error(await res.text());
446
-
447
- currentDocContent = content;
448
-
449
- // Re-fetch rendered HTML and update view
450
- const doc = await fetch("/api/documents/" + currentDocId).then((r) =>
451
- r.json(),
452
- );
453
- _lastDocHtml = doc.html;
454
- _lastDocIdRendered = currentDocId;
455
- _wireDocContent(doc.html);
456
-
457
- applyAnnotationHighlights();
458
- renderElevator();
459
-
460
- const fileLinkMatches = content.match(/\]\(\s*\.?\/files\/[^)\s]+/g);
461
- const fileLinkCount = fileLinkMatches ? fileLinkMatches.length : 0;
462
- if (fileLinkCount > 0) fileAttachmentCounts[currentDocId] = fileLinkCount;
463
- else delete fileAttachmentCounts[currentDocId];
464
- refreshSidebar();
482
+ await saveCurrentDocumentContent(content);
465
483
 
466
484
  exitEditMode();
467
485
  } catch (err) {
@@ -70,6 +70,7 @@
70
70
  "doc.validate_btn": "Validate",
71
71
  "doc.validate_title": "Validate this document?",
72
72
  "doc.validate_message": "The frontmatter status will change from \"To be validated\" to \"Accepted\".",
73
+ "doc.validate_worklog_message": "The frontmatter status will change from \"To be validated\" to \"Done\".",
73
74
  "doc.validate_detail_low_accuracy": "Reliability is currently {accuracy}. Confirming will also re-baseline source-file hashes, bringing reliability back to 100%.",
74
75
  "doc.validate_confirm": "Validate",
75
76
  "doc.validate_failed": "Validation failed: ",
@@ -282,8 +283,16 @@
282
283
  "snippet.link_anchor_select_hint": "(pick a heading from the document)",
283
284
  "snippet.link_anchor_no_headings": "No headings detected in this document",
284
285
  "snippet.link_target_doc_label": "Target document",
286
+ "snippet.ordered_list_content_label": "Items",
287
+ "snippet.ordered_list_content_placeholder": "One item per line…",
288
+ "snippet.unordered_list_content_label": "Items",
289
+ "snippet.unordered_list_content_placeholder": "One item per line…",
285
290
  "snippet.code_lang_label": "Language",
286
291
  "snippet.code_lang_hint": "(e.g. javascript, python, bash…)",
292
+ "snippet.code_content_label": "Code",
293
+ "snippet.code_content_placeholder": "Paste or edit code here…",
294
+ "snippet.blockquote_content_label": "Quote",
295
+ "snippet.blockquote_content_placeholder": "Quote text…",
287
296
  "snippet.image_alt_label": "Alt text",
288
297
  "snippet.image_alt_placeholder": "Image description",
289
298
  "snippet.image_url_label": "Image URL",
@@ -295,6 +304,16 @@
295
304
  "snippet.tree_add_btn": "+ Add item",
296
305
  "snippet.markdown_preview_label": "Markdown preview",
297
306
  "snippet.insert_btn": "Insert",
307
+ "snippet.inline_edit_btn": "Edit inline",
308
+ "snippet.inline_modal_title": "Edit inline snippet",
309
+ "snippet.inline_save_btn": "Save",
310
+ "snippet.inline_save_failed": "Inline edit failed: ",
311
+ "snippet.inline_delete_btn": "Delete block",
312
+ "snippet.inline_delete_title": "Delete block",
313
+ "snippet.inline_delete_message": "Delete this block from the document?",
314
+ "snippet.inline_delete_detail": "This action will remove the selected Markdown block.",
315
+ "snippet.inline_delete_confirm_btn": "Delete",
316
+ "snippet.inline_delete_failed": "Inline block deletion failed: ",
298
317
  "snippet.diagram_existing": "Existing diagram",
299
318
  "snippet.diagram_new": "New diagram",
300
319
  "snippet.diagram_select_label": "Select diagram",
@@ -70,6 +70,7 @@
70
70
  "doc.validate_btn": "Valider",
71
71
  "doc.validate_title": "Valider ce document ?",
72
72
  "doc.validate_message": "Le statut frontmatter va passer de « To be validated » à « Accepted ».",
73
+ "doc.validate_worklog_message": "Le statut frontmatter va passer de « To be validated » à « Done ».",
73
74
  "doc.validate_detail_low_accuracy": "La fiabilité actuelle est de {accuracy}. Confirmer va aussi réinitialiser les hashes des fichiers source, ramenant la fiabilité à 100 %.",
74
75
  "doc.validate_confirm": "Valider",
75
76
  "doc.validate_failed": "Échec de la validation : ",
@@ -282,8 +283,16 @@
282
283
  "snippet.link_anchor_select_hint": "(choisissez un titre du document)",
283
284
  "snippet.link_anchor_no_headings": "Aucun titre détecté dans ce document",
284
285
  "snippet.link_target_doc_label": "Document cible",
286
+ "snippet.ordered_list_content_label": "Éléments",
287
+ "snippet.ordered_list_content_placeholder": "Un élément par ligne…",
288
+ "snippet.unordered_list_content_label": "Éléments",
289
+ "snippet.unordered_list_content_placeholder": "Un élément par ligne…",
285
290
  "snippet.code_lang_label": "Langage",
286
291
  "snippet.code_lang_hint": "(ex : javascript, python, bash…)",
292
+ "snippet.code_content_label": "Code",
293
+ "snippet.code_content_placeholder": "Collez ou modifiez le code ici…",
294
+ "snippet.blockquote_content_label": "Citation",
295
+ "snippet.blockquote_content_placeholder": "Texte de la citation…",
287
296
  "snippet.image_alt_label": "Texte alternatif",
288
297
  "snippet.image_alt_placeholder": "Description de l'image",
289
298
  "snippet.image_url_label": "URL de l'image",
@@ -295,6 +304,16 @@
295
304
  "snippet.tree_add_btn": "+ Ajouter un élément",
296
305
  "snippet.markdown_preview_label": "Aperçu du markdown",
297
306
  "snippet.insert_btn": "Insérer",
307
+ "snippet.inline_edit_btn": "Édition inline",
308
+ "snippet.inline_modal_title": "Modifier le snippet inline",
309
+ "snippet.inline_save_btn": "Enregistrer",
310
+ "snippet.inline_save_failed": "Échec de l'édition inline : ",
311
+ "snippet.inline_delete_btn": "Supprimer le bloc",
312
+ "snippet.inline_delete_title": "Supprimer le bloc",
313
+ "snippet.inline_delete_message": "Supprimer ce bloc du document ?",
314
+ "snippet.inline_delete_detail": "Cette action retirera le bloc Markdown sélectionné.",
315
+ "snippet.inline_delete_confirm_btn": "Supprimer",
316
+ "snippet.inline_delete_failed": "Échec de la suppression inline : ",
298
317
  "snippet.diagram_existing": "Diagramme existant",
299
318
  "snippet.diagram_new": "Nouveau diagramme",
300
319
  "snippet.diagram_select_label": "Sélectionner un diagramme",
@@ -48,6 +48,7 @@
48
48
  <script defer src="/documents.js"></script>
49
49
  <script defer src="/misc.js"></script>
50
50
  <script defer src="/snippets.js"></script>
51
+ <script defer src="/inline-snippet-edit.js"></script>
51
52
  <script defer src="/annotations.js"></script>
52
53
  <script defer src="/metadata.js"></script>
53
54
  <script defer src="/accuracy-gauge.js"></script>
@@ -1425,12 +1426,13 @@
1425
1426
  <div
1426
1427
  id="snippets-modal"
1427
1428
  class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50"
1428
- >
1429
+ >
1429
1430
  <div
1431
+ id="snippet-modal-card"
1430
1432
  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"
1431
1433
  >
1432
1434
  <h3 class="text-base font-semibold text-gray-900 dark:text-gray-50">
1433
- <span data-i18n="snippet.modal_title">🧩 Insert a snippet</span>
1435
+ <span id="snippet-modal-title" data-i18n="snippet.modal_title">🧩 Insert a snippet</span>
1434
1436
  </h3>
1435
1437
 
1436
1438
  <div
@@ -1636,6 +1638,44 @@
1636
1638
  </div>
1637
1639
  </div>
1638
1640
 
1641
+ <!-- Panel: ordered-list -->
1642
+ <div id="snip-panel-ordered-list" class="hidden space-y-3">
1643
+ <div class="space-y-1.5">
1644
+ <label
1645
+ data-i18n="snippet.ordered_list_content_label"
1646
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1647
+ >Items</label
1648
+ >
1649
+ <textarea
1650
+ id="snip-ordered-list-content"
1651
+ rows="10"
1652
+ data-i18n-placeholder="snippet.ordered_list_content_placeholder"
1653
+ placeholder="One item per line…"
1654
+ oninput="snippetUpdatePreview()"
1655
+ 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 leading-relaxed"
1656
+ ></textarea>
1657
+ </div>
1658
+ </div>
1659
+
1660
+ <!-- Panel: unordered-list -->
1661
+ <div id="snip-panel-unordered-list" class="hidden space-y-3">
1662
+ <div class="space-y-1.5">
1663
+ <label
1664
+ data-i18n="snippet.unordered_list_content_label"
1665
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1666
+ >Items</label
1667
+ >
1668
+ <textarea
1669
+ id="snip-unordered-list-content"
1670
+ rows="10"
1671
+ data-i18n-placeholder="snippet.unordered_list_content_placeholder"
1672
+ placeholder="One item per line…"
1673
+ oninput="snippetUpdatePreview()"
1674
+ 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 leading-relaxed"
1675
+ ></textarea>
1676
+ </div>
1677
+ </div>
1678
+
1639
1679
  <!-- Panel: code-block -->
1640
1680
  <div id="snip-panel-code-block" class="hidden space-y-3">
1641
1681
  <div class="space-y-1.5">
@@ -1654,6 +1694,40 @@
1654
1694
  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"
1655
1695
  />
1656
1696
  </div>
1697
+ <div class="space-y-1.5">
1698
+ <label
1699
+ data-i18n="snippet.code_content_label"
1700
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1701
+ >Code</label
1702
+ >
1703
+ <textarea
1704
+ id="snip-code-content"
1705
+ rows="12"
1706
+ data-i18n-placeholder="snippet.code_content_placeholder"
1707
+ placeholder="Paste or edit code here…"
1708
+ oninput="snippetUpdatePreview()"
1709
+ 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 leading-relaxed"
1710
+ ></textarea>
1711
+ </div>
1712
+ </div>
1713
+
1714
+ <!-- Panel: blockquote -->
1715
+ <div id="snip-panel-blockquote" class="hidden space-y-3">
1716
+ <div class="space-y-1.5">
1717
+ <label
1718
+ data-i18n="snippet.blockquote_content_label"
1719
+ class="block text-xs font-medium text-gray-500 dark:text-gray-400"
1720
+ >Quote</label
1721
+ >
1722
+ <textarea
1723
+ id="snip-blockquote-content"
1724
+ rows="8"
1725
+ data-i18n-placeholder="snippet.blockquote_content_placeholder"
1726
+ placeholder="Quote text…"
1727
+ oninput="snippetUpdatePreview()"
1728
+ 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 leading-relaxed"
1729
+ ></textarea>
1730
+ </div>
1657
1731
  </div>
1658
1732
 
1659
1733
  <!-- Panel: image -->
@@ -2045,7 +2119,7 @@
2045
2119
  </div>
2046
2120
 
2047
2121
  <!-- Markdown preview -->
2048
- <div class="space-y-1.5">
2122
+ <div id="snippet-preview-wrap" class="space-y-1.5">
2049
2123
  <label
2050
2124
  data-i18n="snippet.markdown_preview_label"
2051
2125
  class="block text-xs font-medium text-gray-500 dark:text-gray-400"
@@ -2066,8 +2140,17 @@
2066
2140
  >
2067
2141
  Cancel
2068
2142
  </button>
2143
+ <button
2144
+ onclick="deleteInlineSnippetBlock()"
2145
+ id="snippet-delete-btn"
2146
+ data-i18n="snippet.inline_delete_btn"
2147
+ class="hidden text-sm px-4 py-2 rounded-lg border border-red-200 dark:border-red-800 text-red-600 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 font-semibold transition-colors"
2148
+ >
2149
+ Delete block
2150
+ </button>
2069
2151
  <button
2070
2152
  onclick="insertSnippet()"
2153
+ id="snippet-submit-btn"
2071
2154
  data-i18n="snippet.insert_btn"
2072
2155
  class="text-sm px-4 py-2 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors"
2073
2156
  >
@@ -0,0 +1,219 @@
1
+ // ── Inline snippet editing from rendered viewer ─────────────────────────────
2
+ // Right-clicking a rendered fragment that maps to a known snippet offers an
3
+ // inline edit action. The actual form stays the Snippets modal; this file only
4
+ // maps rendered DOM elements back to source Markdown ranges.
5
+
6
+ const _INLINE_SNIPPET_TYPES = new Set([
7
+ "anchor-doc-link",
8
+ "doc-link",
9
+ "anchor-link",
10
+ "image",
11
+ "link",
12
+ "collapsible",
13
+ "colored-text",
14
+ "colored-section",
15
+ "tree",
16
+ "code-block",
17
+ "table",
18
+ "blockquote",
19
+ "separator",
20
+ "ordered-list",
21
+ "unordered-list",
22
+ ]);
23
+
24
+ const _INLINE_TYPE_SELECTORS = [
25
+ { types: ["collapsible"], selector: "details" },
26
+ { types: ["colored-section"], selector: 'div[style*="border-left"]' },
27
+ { types: ["colored-text"], selector: 'span[style*="color"]' },
28
+ { types: ["tree", "code-block"], selector: "pre" },
29
+ { types: ["table"], selector: "table" },
30
+ { types: ["blockquote"], selector: "blockquote" },
31
+ { types: ["separator"], selector: "hr" },
32
+ { types: ["ordered-list"], selector: "ol" },
33
+ { types: ["unordered-list"], selector: "ul" },
34
+ { types: ["image"], selector: "img" },
35
+ {
36
+ types: ["anchor-doc-link", "doc-link", "anchor-link", "link"],
37
+ selector: "a[href]",
38
+ },
39
+ ];
40
+
41
+ let _inlineSnippetPopup = null;
42
+
43
+ function _inlineRangesOverlap(a, b) {
44
+ return a.start < b.end && b.start < a.end;
45
+ }
46
+
47
+ function _inlineAddRange(ranges, start, raw) {
48
+ if (!raw || !raw.trim()) return;
49
+ const type = detectSnippetType(raw);
50
+ if (!type || !_INLINE_SNIPPET_TYPES.has(type)) return;
51
+ const end = start + raw.length;
52
+ const candidate = { start, end, type };
53
+ if (ranges.some((existing) => _inlineRangesOverlap(existing, candidate))) {
54
+ return;
55
+ }
56
+ ranges.push(candidate);
57
+ }
58
+
59
+ function _inlineAddRegexRanges(ranges, content, regex, groupIndex = 0) {
60
+ let match;
61
+ while ((match = regex.exec(content))) {
62
+ const raw = match[groupIndex];
63
+ const start =
64
+ match.index + (groupIndex > 0 ? match[0].indexOf(raw) : 0);
65
+ _inlineAddRange(ranges, start, raw);
66
+ if (match[0].length === 0) regex.lastIndex += 1;
67
+ }
68
+ }
69
+
70
+ function _inlineCollectSnippetRanges(content) {
71
+ const ranges = [];
72
+
73
+ _inlineAddRegexRanges(ranges, content, /```[\s\S]*?```/g);
74
+ _inlineAddRegexRanges(ranges, content, /<details[\s\S]*?<\/details>/gi);
75
+ _inlineAddRegexRanges(
76
+ ranges,
77
+ content,
78
+ /<div\b[^>]*border-left[^>]*>[\s\S]*?<\/div>/gi,
79
+ );
80
+ _inlineAddRegexRanges(
81
+ ranges,
82
+ content,
83
+ /<span\b[^>]*color:[^>]*>[\s\S]*?<\/span>/gi,
84
+ );
85
+ _inlineAddRegexRanges(
86
+ ranges,
87
+ content,
88
+ /(?:^|\n)((?:\|[^\n]*\|\n)\|[ \t:|-]*\|(?:\n\|[^\n]*\|)*)/g,
89
+ 1,
90
+ );
91
+ _inlineAddRegexRanges(ranges, content, /^> .*(?:\n>.*)*/gm);
92
+ _inlineAddRegexRanges(ranges, content, /^1\. .*(?:\n(?:\d+\.| {3,}\d+\.) .*)*/gm);
93
+ _inlineAddRegexRanges(ranges, content, /^- .*(?:\n(?:- | {2,}- ).*)*/gm);
94
+ _inlineAddRegexRanges(ranges, content, /!\[[^\]\n]*\]\([^)]+\)/g);
95
+ _inlineAddRegexRanges(ranges, content, /\[[^\]\n]+\]\([^)]+\)/g);
96
+
97
+ const frontmatter = content.match(/^---\s*\n[\s\S]*?\n---/);
98
+ _inlineAddRegexRanges(ranges, content, /^---$/gm);
99
+ return ranges
100
+ .filter((range) => {
101
+ if (range.type !== "separator") return true;
102
+ return !frontmatter || range.start > frontmatter[0].length;
103
+ })
104
+ .sort((a, b) => a.start - b.start);
105
+ }
106
+
107
+ function _inlineTopLevelListElements(contentEl, selector) {
108
+ return Array.from(contentEl.querySelectorAll(selector)).filter(
109
+ (el) => !el.parentElement.closest("ol, ul"),
110
+ );
111
+ }
112
+
113
+ function _inlineAssignElements(contentEl, candidates) {
114
+ contentEl
115
+ .querySelectorAll("[data-inline-snippet-index]")
116
+ .forEach((el) => {
117
+ el.removeAttribute("data-inline-snippet-index");
118
+ el.classList.remove("ld-inline-snippet-target");
119
+ });
120
+
121
+ for (const { types, selector } of _INLINE_TYPE_SELECTORS) {
122
+ const matching = candidates.filter((candidate) =>
123
+ types.includes(candidate.type),
124
+ );
125
+ if (!matching.length) continue;
126
+ const elements =
127
+ selector === "ol" || selector === "ul"
128
+ ? _inlineTopLevelListElements(contentEl, selector)
129
+ : Array.from(contentEl.querySelectorAll(selector));
130
+ const limit = Math.min(matching.length, elements.length);
131
+ for (let i = 0; i < limit; i += 1) {
132
+ elements[i].dataset.inlineSnippetIndex = String(
133
+ candidates.indexOf(matching[i]),
134
+ );
135
+ elements[i].classList.add("ld-inline-snippet-target");
136
+ }
137
+ }
138
+ }
139
+
140
+ function _inlineClosePopup() {
141
+ if (_inlineSnippetPopup) {
142
+ _inlineSnippetPopup.remove();
143
+ _inlineSnippetPopup = null;
144
+ }
145
+ }
146
+
147
+ function _inlineShowPopup(event, range) {
148
+ _inlineClosePopup();
149
+ const popup = document.createElement("div");
150
+ popup.id = "inline-snippet-popup";
151
+ popup.className =
152
+ "fixed z-50 rounded-lg border border-blue-200 dark:border-blue-800 bg-white dark:bg-gray-900 shadow-lg p-1";
153
+ const btn = document.createElement("button");
154
+ btn.type = "button";
155
+ btn.className =
156
+ "inline-flex items-center gap-2 rounded-md px-3 py-2 text-sm font-semibold text-blue-700 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/30";
157
+ const icon = document.createElement("i");
158
+ icon.className = "fa-solid fa-pen-to-square";
159
+ icon.setAttribute("aria-hidden", "true");
160
+ const label = document.createElement("span");
161
+ label.textContent = window.t("snippet.inline_edit_btn");
162
+ btn.append(icon, label);
163
+ btn.addEventListener("click", (e) => {
164
+ e.preventDefault();
165
+ e.stopPropagation();
166
+ _inlineClosePopup();
167
+ openSnippetsModalForInlineEdit(range);
168
+ });
169
+ popup.appendChild(btn);
170
+ document.body.appendChild(popup);
171
+
172
+ const margin = 8;
173
+ const rect = popup.getBoundingClientRect();
174
+ const left = Math.min(
175
+ window.innerWidth - rect.width - margin,
176
+ Math.max(margin, event.clientX),
177
+ );
178
+ const top = Math.max(margin, event.clientY - rect.height - margin);
179
+ popup.style.left = `${left}px`;
180
+ popup.style.top = `${top}px`;
181
+ _inlineSnippetPopup = popup;
182
+ }
183
+
184
+ function initInlineSnippetEditing(contentEl) {
185
+ if (!contentEl || typeof currentDocContent !== "string") return;
186
+ const candidates = _inlineCollectSnippetRanges(currentDocContent);
187
+ _inlineAssignElements(contentEl, candidates);
188
+
189
+ if (contentEl._inlineSnippetContextHandler) {
190
+ contentEl.removeEventListener(
191
+ "contextmenu",
192
+ contentEl._inlineSnippetContextHandler,
193
+ );
194
+ }
195
+ contentEl._inlineSnippetContextHandler = (event) => {
196
+ const target = event.target.closest("[data-inline-snippet-index]");
197
+ if (!target || !contentEl.contains(target)) return;
198
+ const range = candidates[Number(target.dataset.inlineSnippetIndex)];
199
+ if (!range) return;
200
+ event.preventDefault();
201
+ _inlineShowPopup(event, range);
202
+ };
203
+ contentEl.addEventListener(
204
+ "contextmenu",
205
+ contentEl._inlineSnippetContextHandler,
206
+ );
207
+ }
208
+
209
+ document.addEventListener("click", (event) => {
210
+ if (_inlineSnippetPopup && !_inlineSnippetPopup.contains(event.target)) {
211
+ _inlineClosePopup();
212
+ }
213
+ });
214
+
215
+ document.addEventListener("keydown", (event) => {
216
+ if (event.key === "Escape") _inlineClosePopup();
217
+ });
218
+
219
+ window.initInlineSnippetEditing = initInlineSnippetEditing;
@@ -6,13 +6,17 @@
6
6
 
7
7
  let _snippetSelStart = 0;
8
8
  let _snippetSelEnd = 0;
9
+ let _snippetInlineEdit = false;
9
10
  const _SNIPPET_PANELS = [
10
11
  "collapsible",
11
12
  "link",
12
13
  "doc-link",
13
14
  "anchor-link",
14
15
  "anchor-doc-link",
16
+ "ordered-list",
17
+ "unordered-list",
15
18
  "code-block",
19
+ "blockquote",
16
20
  "image",
17
21
  "table",
18
22
  "tree",
@@ -626,11 +630,38 @@ async function snippetAnchorDocChanged() {
626
630
  snippetUpdatePreview();
627
631
  }
628
632
 
629
- function openSnippetsModal() {
630
- const editor = document.getElementById("doc-editor");
631
- _snippetSelStart = editor.selectionStart;
632
- _snippetSelEnd = editor.selectionEnd;
633
+ function _setSnippetModalMode(isInlineEdit) {
634
+ _snippetInlineEdit = !!isInlineEdit;
635
+ const title = document.getElementById("snippet-modal-title");
636
+ if (title) {
637
+ title.textContent = window.t(
638
+ _snippetInlineEdit ? "snippet.inline_modal_title" : "snippet.modal_title",
639
+ );
640
+ }
641
+ const submit = document.getElementById("snippet-submit-btn");
642
+ if (submit) {
643
+ submit.textContent = window.t(
644
+ _snippetInlineEdit ? "snippet.inline_save_btn" : "snippet.insert_btn",
645
+ );
646
+ }
647
+ const typeSelect = document.getElementById("snippet-type");
648
+ if (typeSelect) {
649
+ typeSelect.disabled = _snippetInlineEdit;
650
+ typeSelect.classList.toggle("cursor-not-allowed", _snippetInlineEdit);
651
+ typeSelect.classList.toggle("opacity-70", _snippetInlineEdit);
652
+ }
653
+ const deleteBtn = document.getElementById("snippet-delete-btn");
654
+ if (deleteBtn) {
655
+ deleteBtn.classList.toggle("hidden", !_snippetInlineEdit);
656
+ }
657
+ const card = document.getElementById("snippet-modal-card");
658
+ if (card) {
659
+ card.classList.toggle("max-w-lg", !_snippetInlineEdit);
660
+ card.classList.toggle("max-w-5xl", _snippetInlineEdit);
661
+ }
662
+ }
633
663
 
664
+ function _openSnippetsModalForText(selectedText, detectedOverride = null) {
634
665
  const docOpts = allDocs
635
666
  .map((d) => `<option value="${d.id}">${d.title}</option>`)
636
667
  .join("");
@@ -640,13 +671,9 @@ function openSnippetsModal() {
640
671
  snippetAnchorDocChanged();
641
672
 
642
673
  const msgEl = document.getElementById("snippet-detect-msg");
643
- const selectedText = editor.value.slice(
644
- _snippetSelStart,
645
- _snippetSelEnd,
646
- );
647
674
 
648
675
  if (selectedText) {
649
- const detected = detectSnippetType(selectedText);
676
+ const detected = detectedOverride || detectSnippetType(selectedText);
650
677
  if (detected) {
651
678
  document.getElementById("snippet-type").value = detected;
652
679
  snippetTypeChanged();
@@ -688,8 +715,26 @@ function openSnippetsModal() {
688
715
  document.getElementById("snippets-modal").classList.remove("hidden");
689
716
  }
690
717
 
718
+ function openSnippetsModal() {
719
+ const editor = document.getElementById("doc-editor");
720
+ _snippetSelStart = editor.selectionStart;
721
+ _snippetSelEnd = editor.selectionEnd;
722
+ _setSnippetModalMode(false);
723
+ _openSnippetsModalForText(editor.value.slice(_snippetSelStart, _snippetSelEnd));
724
+ }
725
+
726
+ function openSnippetsModalForInlineEdit(range) {
727
+ if (!range || typeof currentDocContent !== "string") return;
728
+ _snippetSelStart = range.start;
729
+ _snippetSelEnd = range.end;
730
+ _setSnippetModalMode(true);
731
+ const selectedText = currentDocContent.slice(_snippetSelStart, _snippetSelEnd);
732
+ _openSnippetsModalForText(selectedText, range.type || null);
733
+ }
734
+
691
735
  function closeSnippetsModal() {
692
736
  document.getElementById("snippets-modal").classList.add("hidden");
737
+ _setSnippetModalMode(false);
693
738
  }
694
739
 
695
740
  function snippetTypeChanged() {
@@ -698,8 +743,21 @@ function snippetTypeChanged() {
698
743
  const panel = document.getElementById("snip-panel-" + p);
699
744
  if (panel) panel.classList.toggle("hidden", p !== type);
700
745
  });
701
- const previewWrap = document.getElementById("snippet-preview")?.parentElement;
702
- if (previewWrap) previewWrap.classList.toggle("hidden", type === "attachment");
746
+ const previewWrap = document.getElementById("snippet-preview-wrap");
747
+ if (previewWrap) {
748
+ previewWrap.classList.toggle(
749
+ "hidden",
750
+ type === "attachment" ||
751
+ type === "table" ||
752
+ type === "code-block" ||
753
+ type === "blockquote" ||
754
+ type === "ordered-list" ||
755
+ type === "unordered-list" ||
756
+ type === "colored-section" ||
757
+ type === "colored-text" ||
758
+ type === "tree",
759
+ );
760
+ }
703
761
 
704
762
  if (type === "table") tableInit();
705
763
  else if (type === "tree") treeInit();
@@ -821,32 +879,70 @@ function buildSnippetMarkdown() {
821
879
  window.t('snippet.link_anchor_placeholder');
822
880
  return `[${text}](?doc=${encodeURIComponent(docId)}#${anchor})`;
823
881
  }
824
- case "ordered-list":
825
- return [
826
- "1. Élément 1",
827
- "2. Élément 2",
828
- " 1. Sous-élément 2.1",
829
- " 2. Sous-élément 2.2",
830
- "3. Élément 3",
831
- " 1. Sous-élément 3.1",
832
- " 1. Sous-sous-élément 3.1.1",
833
- ].join("\n");
834
- case "unordered-list":
835
- return [
836
- "- Élément 1",
837
- "- Élément 2",
838
- " - Sous-élément 2.1",
839
- " - Sous-élément 2.2",
840
- "- Élément 3",
841
- " - Sous-élément 3.1",
842
- " - Sous-sous-élément 3.1.1",
843
- ].join("\n");
882
+ case "ordered-list": {
883
+ const content =
884
+ document.getElementById("snip-ordered-list-content").value ||
885
+ [
886
+ "Élément 1",
887
+ "Élément 2",
888
+ " Sous-élément 2.1",
889
+ " Sous-élément 2.2",
890
+ "Élément 3",
891
+ " Sous-élément 3.1",
892
+ " Sous-sous-élément 3.1.1",
893
+ ].join("\n");
894
+ const countersByIndent = new Map();
895
+ return content
896
+ .split("\n")
897
+ .filter((line) => line.trim())
898
+ .map((line) => {
899
+ const indent = line.match(/^\s*/)[0];
900
+ const indentLength = indent.length;
901
+ for (const knownIndent of Array.from(countersByIndent.keys())) {
902
+ if (knownIndent > indentLength) countersByIndent.delete(knownIndent);
903
+ }
904
+ const next = (countersByIndent.get(indentLength) || 0) + 1;
905
+ countersByIndent.set(indentLength, next);
906
+ return `${indent}${next}. ${line.trim()}`;
907
+ })
908
+ .join("\n");
909
+ }
910
+ case "unordered-list": {
911
+ const content =
912
+ document.getElementById("snip-unordered-list-content").value ||
913
+ [
914
+ "Élément 1",
915
+ "Élément 2",
916
+ " Sous-élément 2.1",
917
+ " Sous-élément 2.2",
918
+ "Élément 3",
919
+ " Sous-élément 3.1",
920
+ " Sous-sous-élément 3.1.1",
921
+ ].join("\n");
922
+ return content
923
+ .split("\n")
924
+ .filter((line) => line.trim())
925
+ .map((line) => {
926
+ const indent = line.match(/^\s*/)[0];
927
+ return `${indent}- ${line.trim()}`;
928
+ })
929
+ .join("\n");
930
+ }
844
931
  case "code-block": {
845
932
  const lang = document.getElementById("snip-code-lang").value || "";
846
- return `\`\`\`${lang}\n// code ici\n\`\`\``;
933
+ const code =
934
+ document.getElementById("snip-code-content").value || "// code ici";
935
+ return `\`\`\`${lang}\n${code}\n\`\`\``;
936
+ }
937
+ case "blockquote": {
938
+ const content =
939
+ document.getElementById("snip-blockquote-content").value ||
940
+ "Citation ici\n\n— Auteur";
941
+ return content
942
+ .split("\n")
943
+ .map((line) => (line.trim() ? `> ${line}` : ">"))
944
+ .join("\n");
847
945
  }
848
- case "blockquote":
849
- return `> Citation ici\n>\n> — Auteur`;
850
946
  case "separator":
851
947
  return `\n---\n`;
852
948
  case "image": {
@@ -903,8 +999,11 @@ function snippetUpdatePreview() {
903
999
  buildSnippetMarkdown();
904
1000
  }
905
1001
 
906
- function insertSnippet() {
1002
+ async function insertSnippet() {
907
1003
  const type = document.getElementById("snippet-type").value;
1004
+ if (_snippetInlineEdit && (type === "diagram" || type === "attachment")) {
1005
+ return;
1006
+ }
908
1007
  if (type === "diagram") {
909
1008
  insertDiagramSnippet();
910
1009
  return;
@@ -915,7 +1014,21 @@ function insertSnippet() {
915
1014
  return;
916
1015
  }
917
1016
  const text = buildSnippetMarkdown();
1017
+ const wasInlineEdit = _snippetInlineEdit;
918
1018
  closeSnippetsModal();
1019
+ if (wasInlineEdit) {
1020
+ const before = currentDocContent.slice(0, _snippetSelStart);
1021
+ const after = currentDocContent.slice(_snippetSelEnd);
1022
+ try {
1023
+ await saveCurrentDocumentContent(before + text + after);
1024
+ } catch (err) {
1025
+ alert(
1026
+ window.t("snippet.inline_save_failed") +
1027
+ (err && err.message ? err.message : String(err)),
1028
+ );
1029
+ }
1030
+ return;
1031
+ }
919
1032
  const editor = document.getElementById("doc-editor");
920
1033
  const before = editor.value.slice(0, _snippetSelStart);
921
1034
  const after = editor.value.slice(_snippetSelEnd);
@@ -925,6 +1038,34 @@ function insertSnippet() {
925
1038
  editor.focus();
926
1039
  }
927
1040
 
1041
+ async function deleteInlineSnippetBlock() {
1042
+ if (!_snippetInlineEdit) return;
1043
+ const ok =
1044
+ typeof showConfirm === "function"
1045
+ ? await showConfirm({
1046
+ title: window.t("snippet.inline_delete_title"),
1047
+ message: window.t("snippet.inline_delete_message"),
1048
+ detail: window.t("snippet.inline_delete_detail"),
1049
+ confirmLabel: window.t("snippet.inline_delete_confirm_btn"),
1050
+ danger: true,
1051
+ detailTone: "warning",
1052
+ })
1053
+ : confirm(window.t("snippet.inline_delete_message"));
1054
+ if (!ok) return;
1055
+
1056
+ const before = currentDocContent.slice(0, _snippetSelStart);
1057
+ const after = currentDocContent.slice(_snippetSelEnd);
1058
+ closeSnippetsModal();
1059
+ try {
1060
+ await saveCurrentDocumentContent(before + after);
1061
+ } catch (err) {
1062
+ alert(
1063
+ window.t("snippet.inline_delete_failed") +
1064
+ (err && err.message ? err.message : String(err)),
1065
+ );
1066
+ }
1067
+ }
1068
+
928
1069
  async function insertDiagramSnippet() {
929
1070
  const isNew = document.getElementById("snip-diag-mode-new").checked;
930
1071
  const imgName =
@@ -979,6 +1120,22 @@ async function insertDiagramSnippet() {
979
1120
  }
980
1121
 
981
1122
  // ── Snippet parsing (detection lives in /snippet-detect.js) ────────────────
1123
+ function _parseMarkdownTableCells(line) {
1124
+ return line
1125
+ .trim()
1126
+ .split("|")
1127
+ .slice(1, -1)
1128
+ .map((cell) => cell.trim());
1129
+ }
1130
+
1131
+ function _isMarkdownTableSeparatorLine(line) {
1132
+ const cells = _parseMarkdownTableCells(line);
1133
+ return (
1134
+ cells.length > 0 &&
1135
+ cells.every((cell) => /^:?-{3,}:?$/.test(cell.replace(/\s+/g, "")))
1136
+ );
1137
+ }
1138
+
982
1139
  function parseAndFillSnippet(text, type) {
983
1140
  const t = text.trim();
984
1141
  switch (type) {
@@ -1065,9 +1222,31 @@ function parseAndFillSnippet(text, type) {
1065
1222
  }
1066
1223
  break;
1067
1224
  }
1225
+ case "ordered-list": {
1226
+ document.getElementById("snip-ordered-list-content").value = t
1227
+ .split("\n")
1228
+ .map((line) => line.replace(/^(\s*)\d+\.\s?/, "$1"))
1229
+ .join("\n");
1230
+ break;
1231
+ }
1232
+ case "unordered-list": {
1233
+ document.getElementById("snip-unordered-list-content").value = t
1234
+ .split("\n")
1235
+ .map((line) => line.replace(/^(\s*)[-*+]\s?/, "$1"))
1236
+ .join("\n");
1237
+ break;
1238
+ }
1068
1239
  case "code-block": {
1069
- const m = t.match(/^```(\w*)\n/);
1070
- document.getElementById("snip-code-lang").value = m ? m[1] : "";
1240
+ const m = t.match(/^```\s*([^\n]*)\n([\s\S]*?)\n```$/);
1241
+ document.getElementById("snip-code-lang").value = m ? m[1].trim() : "";
1242
+ document.getElementById("snip-code-content").value = m ? m[2] : "";
1243
+ break;
1244
+ }
1245
+ case "blockquote": {
1246
+ document.getElementById("snip-blockquote-content").value = t
1247
+ .split("\n")
1248
+ .map((line) => line.replace(/^>\s?/, ""))
1249
+ .join("\n");
1071
1250
  break;
1072
1251
  }
1073
1252
  case "image": {
@@ -1083,14 +1262,9 @@ function parseAndFillSnippet(text, type) {
1083
1262
  .split("\n")
1084
1263
  .filter((l) => /^\|.*\|$/.test(l.trim()));
1085
1264
  const dataLines = allLines.filter(
1086
- (l) => !/^\| *[-: ][-| :]*\|/.test(l),
1087
- );
1088
- _tableData = dataLines.map((line) =>
1089
- line
1090
- .split("|")
1091
- .slice(1, -1)
1092
- .map((c) => c.trim()),
1265
+ (l) => !_isMarkdownTableSeparatorLine(l),
1093
1266
  );
1267
+ _tableData = dataLines.map(_parseMarkdownTableCells);
1094
1268
  const maxCols = Math.max(..._tableData.map((r) => r.length));
1095
1269
  _tableData.forEach((row) => {
1096
1270
  while (row.length < maxCols) row.push("");
@@ -5,15 +5,21 @@
5
5
  // below 100%, the confirmation modal also warns that source-file hashes will be
6
6
  // re-baselined, and POST /api/metadata/:id/refresh is called after the PUT.
7
7
 
8
- // Frontmatter on this project is a `---`-fenced block of `**key:** value` lines
9
- // (not standard YAML). The status line is the only field this module touches.
10
- const _STATUS_LINE_RE = /^(\*\*status:\*\*\s*).+?\s*$/m;
8
+ // Frontmatter may use this project's historical `**key:** value` convention or
9
+ // regular YAML-style `key: value` lines. The status line is the only field this
10
+ // module touches.
11
+ const _STATUS_LINE_RE = /^(\s*(?:\*\*status:\*\*|status:)\s*).+?\s*$/im;
12
+
13
+ function isWorklogDocument(docId) {
14
+ if (/%5BWORKLOG%5D/i.test(docId)) return true;
15
+ return false;
16
+ }
11
17
 
12
18
  function getDocStatus(content) {
13
19
  if (typeof content !== "string") return null;
14
20
  const fence = content.match(/^---\s*\n([\s\S]*?)\n---/);
15
21
  if (!fence) return null;
16
- const m = fence[1].match(/^\*\*status:\*\*\s*(.+?)\s*$/m);
22
+ const m = fence[1].match(/^\s*(?:\*\*status:\*\*|status:)\s*(.+?)\s*$/im);
17
23
  return m ? m[1].trim() : null;
18
24
  }
19
25
 
@@ -27,7 +33,7 @@ function updateValidateButtonForCurrentDoc() {
27
33
  const status = getDocStatus(
28
34
  typeof currentDocContent !== "undefined" ? currentDocContent : "",
29
35
  );
30
- if (status === "To be validated") {
36
+ if (status && status.toUpperCase() === "TO BE VALIDATED") {
31
37
  btn.classList.remove("hidden");
32
38
  } else {
33
39
  btn.classList.add("hidden");
@@ -53,12 +59,18 @@ async function validateCurrentDoc() {
53
59
  const pct = Math.round(accuracy * 100);
54
60
  const lowAccuracy = pct < 100;
55
61
  const detail = lowAccuracy
56
- ? window.t("doc.validate_detail_low_accuracy").replace("{accuracy}", pct + "%")
62
+ ? window
63
+ .t("doc.validate_detail_low_accuracy")
64
+ .replace("{accuracy}", pct + "%")
57
65
  : "";
58
66
 
59
67
  const ok = await window.showConfirm({
60
68
  title: window.t("doc.validate_title"),
61
- message: window.t("doc.validate_message"),
69
+ message: window.t(
70
+ isWorklogDocument(currentDocId)
71
+ ? "doc.validate_worklog_message"
72
+ : "doc.validate_message",
73
+ ),
62
74
  detail,
63
75
  detailTone: lowAccuracy ? "warning" : undefined,
64
76
  confirmLabel: window.t("doc.validate_confirm"),
@@ -69,7 +81,10 @@ async function validateCurrentDoc() {
69
81
  if (btn) btn.disabled = true;
70
82
 
71
83
  try {
72
- const newContent = _replaceStatus(currentDocContent, "Accepted");
84
+ const newContent = _replaceStatus(
85
+ currentDocContent,
86
+ isWorklogDocument(currentDocId) ? "Done" : "Accepted",
87
+ );
73
88
  if (newContent === currentDocContent) {
74
89
  throw new Error("status line not found in frontmatter");
75
90
  }
@@ -16,7 +16,7 @@ exports.assertNotSuperSeeded = assertNotSuperSeeded;
16
16
  const fs_1 = __importDefault(require("fs"));
17
17
  const path_1 = __importDefault(require("path"));
18
18
  const config_1 = require("./config");
19
- const STATUS_LINE_RE = /^\*\*status:\*\*\s*(.+?)\s*$/m;
19
+ const STATUS_LINE_RE = /^\s*(?:\*\*status:\*\*|status:)\s*(.+?)\s*$/im;
20
20
  const FRONTMATTER_FENCE_RE = /^---\s*\n([\s\S]*?)\n---/;
21
21
  function parseDocStatus(content) {
22
22
  if (typeof content !== "string")
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/lib/status.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;;;;;;AAS1E,wCAMC;AAmBD,sCAYC;AAiBD,oDAQC;AArED,4CAAoB;AACpB,gDAAwB;AACxB,qCAAsC;AAEtC,MAAM,cAAc,GAAG,+BAA+B,CAAC;AACvD,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAExD,SAAgB,cAAc,CAAC,OAAe;IAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,SAAS,kBAAkB,CACzB,QAAgB,EAChB,YAAoB,EACpB,UAAoB;IAEpB,IAAI,cAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;QACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3D,CAAC;IACD,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,cAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAgB,aAAa,CAC3B,QAAgB,EAChB,YAAoB,EACpB,UAAoB;IAEpB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;IACxE,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAEY,QAAA,kBAAkB,GAAG,aAAa,CAAC;AAEhD,2EAA2E;AAC3E,8EAA8E;AAC9E,gEAAgE;AAChE,MAAa,wBAAyB,SAAQ,KAAK;IAEjD,YACE,OAAO,GAAG,iDAAiD;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,SAAI,GAAG,sBAAsB,CAAC;QAKrC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AARD,4DAQC;AAED,SAAgB,oBAAoB,CAClC,QAAgB,EAChB,YAAoB;IAEpB,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,KAAK,0BAAkB,EAAE,CAAC;QAC7E,MAAM,IAAI,wBAAwB,EAAE,CAAC;IACvC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/lib/status.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;;;;;;AAS1E,wCAMC;AAmBD,sCAYC;AAiBD,oDAQC;AArED,4CAAoB;AACpB,gDAAwB;AACxB,qCAAsC;AAEtC,MAAM,cAAc,GAAG,+CAA+C,CAAC;AACvE,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAExD,SAAgB,cAAc,CAAC,OAAe;IAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,SAAS,kBAAkB,CACzB,QAAgB,EAChB,YAAoB,EACpB,UAAoB;IAEpB,IAAI,cAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,YAAY,GAAG,KAAK,CAAC;QACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;IAC3D,CAAC;IACD,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,GAAG,KAAK,CAAC,CAAC;IAC9D,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,cAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAgB,aAAa,CAC3B,QAAgB,EAChB,YAAoB,EACpB,UAAoB;IAEpB,MAAM,QAAQ,GAAG,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;IACxE,IAAI,CAAC,QAAQ,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAEY,QAAA,kBAAkB,GAAG,aAAa,CAAC;AAEhD,2EAA2E;AAC3E,8EAA8E;AAC9E,gEAAgE;AAChE,MAAa,wBAAyB,SAAQ,KAAK;IAEjD,YACE,OAAO,GAAG,iDAAiD;QAE3D,KAAK,CAAC,OAAO,CAAC,CAAC;QAJR,SAAI,GAAG,sBAAsB,CAAC;QAKrC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AARD,4DAQC;AAED,SAAgB,oBAAoB,CAClC,QAAgB,EAChB,YAAoB;IAEpB,MAAM,EAAE,UAAU,GAAG,EAAE,EAAE,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,UAAU,CAAC,KAAK,0BAAkB,EAAE,CAAC;QAC7E,MAAM,IAAI,wBAAwB,EAAE,CAAC;IACvC,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "living-ai-documentation",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Local Markdown documentation hub with a built-in MCP server — coding agents create ADRs, draw diagrams and detect drift while you code.",
5
5
  "main": "dist/src/server.js",
6
6
  "bin": {