living-documentation 7.9.0 → 7.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/frontend/confirm-modal.js +73 -0
- package/dist/src/frontend/documents.js +88 -103
- package/dist/src/frontend/files-modal.js +243 -0
- package/dist/src/frontend/i18n/en.json +51 -16
- package/dist/src/frontend/i18n/fr.json +51 -16
- package/dist/src/frontend/index.html +110 -2
- package/dist/src/frontend/search.js +45 -8
- package/dist/src/frontend/snippets.js +481 -0
- package/dist/src/lib/metadata.d.ts.map +1 -1
- package/dist/src/lib/metadata.js +5 -9
- package/dist/src/lib/metadata.js.map +1 -1
- package/dist/src/routes/documents.d.ts.map +1 -1
- package/dist/src/routes/documents.js +34 -3
- package/dist/src/routes/documents.js.map +1 -1
- package/dist/src/routes/files.d.ts.map +1 -1
- package/dist/src/routes/files.js +100 -0
- package/dist/src/routes/files.js.map +1 -1
- package/dist/starting-doc/1_tutorial/2026_04_11_13_25_[General]_crer_vos_dossiers.md +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common.save": "Enregistrer",
|
|
3
3
|
"common.cancel": "Annuler",
|
|
4
|
+
"common.confirm": "Confirmer",
|
|
4
5
|
"common.reset": "Réinitialiser",
|
|
5
6
|
"common.loading": "Chargement…",
|
|
6
7
|
"common.remove": "Supprimer",
|
|
@@ -28,8 +29,31 @@
|
|
|
28
29
|
"nav.new_document": "Nouveau document",
|
|
29
30
|
"nav.word_cloud": "☁ Nuage de mots",
|
|
30
31
|
"nav.diagram": "◇ Diagramme",
|
|
32
|
+
"nav.files": "📁 Fichiers Métadonnées",
|
|
31
33
|
"nav.admin": "⚙ Admin",
|
|
32
34
|
|
|
35
|
+
"files.title": "📁 Fichiers joints",
|
|
36
|
+
"files.empty": "Aucun fichier — téléversez-en un depuis un document (trombone, glisser-déposer ou coller).",
|
|
37
|
+
"files.replace": "Remplacer",
|
|
38
|
+
"files.delete": "Supprimer",
|
|
39
|
+
"files.open": "Ouvrir",
|
|
40
|
+
"files.confirm_replace": "Remplacer « {name} » par « {newName} » ? La version précédente sera perdue.",
|
|
41
|
+
"files.confirm_replace_title": "Remplacer le fichier",
|
|
42
|
+
"files.confirm_replace_message": "Remplacer « {name} » par « {newName} » ?",
|
|
43
|
+
"files.confirm_replace_detail": "La version précédente sera perdue.",
|
|
44
|
+
"files.confirm_delete": "Supprimer définitivement « {name} » ?",
|
|
45
|
+
"files.confirm_delete_title": "Supprimer le fichier",
|
|
46
|
+
"files.confirm_delete_message": "Supprimer définitivement « {name} » ?",
|
|
47
|
+
"files.confirm_delete_detail": "Cette action est irréversible.",
|
|
48
|
+
"files.replacing": "Remplacement…",
|
|
49
|
+
"files.deleting": "Suppression…",
|
|
50
|
+
"files.error_load": "Impossible de charger les fichiers : ",
|
|
51
|
+
"files.error_replace": "Échec du remplacement : ",
|
|
52
|
+
"files.error_delete": "Échec de la suppression : ",
|
|
53
|
+
"files.size_bytes": "{n} o",
|
|
54
|
+
"files.size_kb": "{n} Ko",
|
|
55
|
+
"files.size_mb": "{n} Mo",
|
|
56
|
+
|
|
33
57
|
"welcome.title": "Sélectionner un document",
|
|
34
58
|
"welcome.hint": "Choisissez un document dans la barre latérale pour commencer à lire.",
|
|
35
59
|
"welcome.pattern_hint": "Modèle de nom de fichier attendu",
|
|
@@ -126,21 +150,21 @@
|
|
|
126
150
|
|
|
127
151
|
"snippet.modal_title": "🧩 Insérer un snippet",
|
|
128
152
|
"snippet.type_label": "Type de snippet",
|
|
129
|
-
"snippet.diagram": "Diagramme",
|
|
130
|
-
"snippet.collapsible": "Bloc repliable (détails)",
|
|
131
|
-
"snippet.link": "Lien",
|
|
132
|
-
"snippet.link_doc": "Lien vers un document",
|
|
133
|
-
"snippet.link_anchor": "Lien vers une ancre",
|
|
134
|
-
"snippet.link_doc_anchor": "Lien vers un document avec ancre",
|
|
135
|
-
"snippet.numbered_list": "Liste numérotée",
|
|
136
|
-
"snippet.bullet_list": "Liste à puces",
|
|
137
|
-
"snippet.code_block": "Bloc de code",
|
|
138
|
-
"snippet.blockquote": "Citation (blockquote)",
|
|
139
|
-
"snippet.separator": "Séparateur horizontal",
|
|
140
|
-
"snippet.image": "Image",
|
|
141
|
-
"snippet.table": "Tableau",
|
|
142
|
-
"snippet.tree": "Arborescence",
|
|
143
|
-
"snippet.colored_section": "Section colorée",
|
|
153
|
+
"snippet.diagram": "◇ Diagramme",
|
|
154
|
+
"snippet.collapsible": "▸ Bloc repliable (détails)",
|
|
155
|
+
"snippet.link": "🔗 Lien",
|
|
156
|
+
"snippet.link_doc": "📄 Lien vers un document",
|
|
157
|
+
"snippet.link_anchor": "⚓ Lien vers une ancre",
|
|
158
|
+
"snippet.link_doc_anchor": "📄⚓ Lien vers un document avec ancre",
|
|
159
|
+
"snippet.numbered_list": "🔢 Liste numérotée",
|
|
160
|
+
"snippet.bullet_list": "• Liste à puces",
|
|
161
|
+
"snippet.code_block": "💻 Bloc de code",
|
|
162
|
+
"snippet.blockquote": "❝ Citation (blockquote)",
|
|
163
|
+
"snippet.separator": "― Séparateur horizontal",
|
|
164
|
+
"snippet.image": "🖼 Image",
|
|
165
|
+
"snippet.table": "▦ Tableau",
|
|
166
|
+
"snippet.tree": "🌳 Arborescence",
|
|
167
|
+
"snippet.colored_section": "🎨 Section colorée",
|
|
144
168
|
"snippet.colored_section_color_label": "Couleur",
|
|
145
169
|
"snippet.colored_section_swatch_info": "Info (bleu)",
|
|
146
170
|
"snippet.colored_section_swatch_success": "Succès (vert)",
|
|
@@ -150,7 +174,7 @@
|
|
|
150
174
|
"snippet.colored_section_swatch_neutral": "Neutre (gris)",
|
|
151
175
|
"snippet.colored_section_content_label": "Contenu",
|
|
152
176
|
"snippet.colored_section_content_placeholder": "Votre texte ici…",
|
|
153
|
-
"snippet.colored_text": "Texte coloré",
|
|
177
|
+
"snippet.colored_text": "🖍 Texte coloré",
|
|
154
178
|
"snippet.colored_text_color_label": "Couleur",
|
|
155
179
|
"snippet.colored_text_content_label": "Texte",
|
|
156
180
|
"snippet.colored_text_content_placeholder": "Votre texte…",
|
|
@@ -197,6 +221,17 @@
|
|
|
197
221
|
"snippet.saving_btn": "Enregistrement…",
|
|
198
222
|
"snippet.detected_msg": "✓ Type détecté : {type}. Les champs ont été pré-remplis — modifiez puis cliquez sur Insérer pour remplacer la sélection.",
|
|
199
223
|
"snippet.unknown_type_msg": "⚠ Le texte sélectionné ne correspond à aucun type de snippet reconnu. Choisissez un type ci-dessous — le snippet remplacera quand même la sélection.",
|
|
224
|
+
"snippet.emojis": "😀 Émojis",
|
|
225
|
+
"snippet.emoji_selected_label": "Émojis sélectionnés",
|
|
226
|
+
"snippet.emoji_clear_btn": "Vider",
|
|
227
|
+
"snippet.emoji_cat_smileys": "Smileys & personnes",
|
|
228
|
+
"snippet.emoji_cat_gestures": "Gestes & corps",
|
|
229
|
+
"snippet.emoji_cat_hearts": "Cœurs & étincelles",
|
|
230
|
+
"snippet.emoji_cat_objects": "Tech & outils",
|
|
231
|
+
"snippet.emoji_cat_office": "Bureau & documents",
|
|
232
|
+
"snippet.emoji_cat_symbols": "Symboles & flèches",
|
|
233
|
+
"snippet.emoji_search_placeholder": "Rechercher un émoji… (ex : coeur, etoile, fusee)",
|
|
234
|
+
"snippet.emoji_no_results": "Aucun émoji ne correspond à cette recherche.",
|
|
200
235
|
"snippet.attachment": "📎 Pièce jointe",
|
|
201
236
|
"snippet.attachment_help": "Cliquez sur <strong>Insérer</strong> pour choisir un fichier. Il sera téléversé dans le dossier <code>files/</code> et inséré sous forme de lien <i class=\"fa-solid fa-paperclip\"></i> dans votre document.",
|
|
202
237
|
"snippet.attachment_alt": "Astuce : vous pouvez aussi glisser-déposer un fichier sur l'éditeur ou le coller depuis le presse-papiers.",
|
|
@@ -54,6 +54,8 @@
|
|
|
54
54
|
<script defer src="/diagram-link-modal.js"></script>
|
|
55
55
|
<script defer src="/new-folder-modal.js"></script>
|
|
56
56
|
<script defer src="/new-doc-modal.js"></script>
|
|
57
|
+
<script defer src="/confirm-modal.js"></script>
|
|
58
|
+
<script defer src="/files-modal.js"></script>
|
|
57
59
|
<script defer src="/boot.js"></script>
|
|
58
60
|
|
|
59
61
|
<script>
|
|
@@ -309,6 +311,15 @@
|
|
|
309
311
|
◇ Diagram
|
|
310
312
|
</a>
|
|
311
313
|
|
|
314
|
+
<!-- Files -->
|
|
315
|
+
<button
|
|
316
|
+
onclick="openFilesModal()"
|
|
317
|
+
data-i18n="nav.files"
|
|
318
|
+
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"
|
|
319
|
+
>
|
|
320
|
+
📁 Files
|
|
321
|
+
</button>
|
|
322
|
+
|
|
312
323
|
<!-- Admin link -->
|
|
313
324
|
<a
|
|
314
325
|
href="/admin"
|
|
@@ -1283,6 +1294,7 @@
|
|
|
1283
1294
|
<option data-i18n="snippet.tree" value="tree">Tree</option>
|
|
1284
1295
|
<option data-i18n="snippet.colored_section" value="colored-section">Colored section</option>
|
|
1285
1296
|
<option data-i18n="snippet.colored_text" value="colored-text">Colored text</option>
|
|
1297
|
+
<option data-i18n="snippet.emojis" value="emojis">😀 Emojis</option>
|
|
1286
1298
|
<option data-i18n="snippet.attachment" value="attachment">📎 File attachment</option>
|
|
1287
1299
|
</select>
|
|
1288
1300
|
</div>
|
|
@@ -1806,13 +1818,52 @@
|
|
|
1806
1818
|
</div>
|
|
1807
1819
|
</div>
|
|
1808
1820
|
|
|
1821
|
+
<!-- Panel: emojis -->
|
|
1822
|
+
<div id="snip-panel-emojis" class="hidden space-y-3">
|
|
1823
|
+
<div class="space-y-1.5">
|
|
1824
|
+
<label
|
|
1825
|
+
data-i18n="snippet.emoji_selected_label"
|
|
1826
|
+
class="block text-xs font-medium text-gray-500 dark:text-gray-400"
|
|
1827
|
+
>Selected emojis</label
|
|
1828
|
+
>
|
|
1829
|
+
<div class="flex gap-1">
|
|
1830
|
+
<input
|
|
1831
|
+
id="snip-emoji-string"
|
|
1832
|
+
type="text"
|
|
1833
|
+
oninput="snippetUpdatePreview()"
|
|
1834
|
+
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 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
1835
|
+
/>
|
|
1836
|
+
<button
|
|
1837
|
+
type="button"
|
|
1838
|
+
id="snip-emoji-clear"
|
|
1839
|
+
onclick="emojiClear()"
|
|
1840
|
+
data-i18n="snippet.emoji_clear_btn"
|
|
1841
|
+
class="px-3 py-2 text-xs rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
1842
|
+
>Clear</button>
|
|
1843
|
+
</div>
|
|
1844
|
+
</div>
|
|
1845
|
+
<div class="space-y-1.5">
|
|
1846
|
+
<input
|
|
1847
|
+
id="snip-emoji-search"
|
|
1848
|
+
type="text"
|
|
1849
|
+
data-i18n-placeholder="snippet.emoji_search_placeholder"
|
|
1850
|
+
placeholder="Search emojis… (e.g. heart, star, rocket)"
|
|
1851
|
+
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"
|
|
1852
|
+
/>
|
|
1853
|
+
</div>
|
|
1854
|
+
<div
|
|
1855
|
+
id="snip-emoji-grid"
|
|
1856
|
+
class="space-y-2 max-h-64 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg p-2 bg-white dark:bg-gray-900"
|
|
1857
|
+
></div>
|
|
1858
|
+
</div>
|
|
1859
|
+
|
|
1809
1860
|
<!-- Panel: file attachment -->
|
|
1810
1861
|
<div id="snip-panel-attachment" class="hidden space-y-3">
|
|
1811
1862
|
<div class="rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 px-3 py-3 text-sm text-blue-900 dark:text-blue-100 space-y-2">
|
|
1812
|
-
<p data-i18n="snippet.attachment_help">
|
|
1863
|
+
<p data-i18n-html="snippet.attachment_help">
|
|
1813
1864
|
Click <strong>Insert</strong> to choose a file. It will be uploaded under the <code>files/</code> folder and inserted as a <i class="fa-solid fa-paperclip"></i> link in your document.
|
|
1814
1865
|
</p>
|
|
1815
|
-
<p data-i18n="snippet.attachment_alt" class="text-xs text-blue-700 dark:text-blue-300">
|
|
1866
|
+
<p data-i18n-html="snippet.attachment_alt" class="text-xs text-blue-700 dark:text-blue-300">
|
|
1816
1867
|
Tip: you can also drag & drop a file onto the editor, or paste it from the clipboard.
|
|
1817
1868
|
</p>
|
|
1818
1869
|
</div>
|
|
@@ -2519,5 +2570,62 @@
|
|
|
2519
2570
|
</div>
|
|
2520
2571
|
</div>
|
|
2521
2572
|
</div>
|
|
2573
|
+
|
|
2574
|
+
<!-- ── Generic confirmation modal (used by showConfirm) ── -->
|
|
2575
|
+
<div
|
|
2576
|
+
id="confirm-modal"
|
|
2577
|
+
class="hidden fixed inset-0 z-[60] bg-black/50 flex items-center justify-center p-4"
|
|
2578
|
+
onclick="_confirmModalBackdrop(event)"
|
|
2579
|
+
>
|
|
2580
|
+
<div
|
|
2581
|
+
class="w-full max-w-md bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-xl shadow-2xl p-5 flex flex-col gap-3"
|
|
2582
|
+
onclick="event.stopPropagation()"
|
|
2583
|
+
>
|
|
2584
|
+
<h3 id="confirm-modal-title" class="font-semibold text-gray-900 dark:text-gray-100"></h3>
|
|
2585
|
+
<p id="confirm-modal-message" class="text-sm text-gray-700 dark:text-gray-200"></p>
|
|
2586
|
+
<p id="confirm-modal-detail" class="text-xs text-gray-500 dark:text-gray-400 italic break-all"></p>
|
|
2587
|
+
<div class="flex justify-end gap-2 mt-2">
|
|
2588
|
+
<button
|
|
2589
|
+
id="confirm-modal-cancel"
|
|
2590
|
+
type="button"
|
|
2591
|
+
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"
|
|
2592
|
+
></button>
|
|
2593
|
+
<button
|
|
2594
|
+
id="confirm-modal-ok"
|
|
2595
|
+
type="button"
|
|
2596
|
+
class="text-sm px-4 py-1.5 rounded-lg text-white font-semibold transition-colors"
|
|
2597
|
+
></button>
|
|
2598
|
+
</div>
|
|
2599
|
+
</div>
|
|
2600
|
+
</div>
|
|
2601
|
+
|
|
2602
|
+
<!-- ── Files modal ── -->
|
|
2603
|
+
<div
|
|
2604
|
+
id="files-modal"
|
|
2605
|
+
class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40"
|
|
2606
|
+
onclick="if(event.target===this)closeFilesModal()"
|
|
2607
|
+
>
|
|
2608
|
+
<div
|
|
2609
|
+
class="bg-white dark:bg-gray-900 rounded-xl shadow-xl w-[min(720px,92vw)] max-h-[85vh] flex flex-col border border-gray-200 dark:border-gray-800"
|
|
2610
|
+
>
|
|
2611
|
+
<div
|
|
2612
|
+
class="flex items-center justify-between px-5 py-3 border-b border-gray-200 dark:border-gray-800 shrink-0"
|
|
2613
|
+
>
|
|
2614
|
+
<h2 data-i18n="files.title" class="font-semibold text-base text-gray-900 dark:text-gray-100">
|
|
2615
|
+
📁 Files
|
|
2616
|
+
</h2>
|
|
2617
|
+
<button
|
|
2618
|
+
onclick="closeFilesModal()"
|
|
2619
|
+
data-i18n-title="common.close"
|
|
2620
|
+
class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 text-xl leading-none px-2"
|
|
2621
|
+
>
|
|
2622
|
+
×
|
|
2623
|
+
</button>
|
|
2624
|
+
</div>
|
|
2625
|
+
<div id="files-modal-body" class="flex-1 overflow-y-auto p-5">
|
|
2626
|
+
<p class="text-sm text-gray-400" data-i18n="common.loading">Loading…</p>
|
|
2627
|
+
</div>
|
|
2628
|
+
</div>
|
|
2629
|
+
</div>
|
|
2522
2630
|
</body>
|
|
2523
2631
|
</html>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// ── Search + result highlighting ─────────────────────────────────────────────
|
|
2
2
|
|
|
3
3
|
let searchTimer = null;
|
|
4
|
+
const METADATA_SEARCH_PREFIX = "metadata://";
|
|
5
|
+
|
|
6
|
+
function _isMetadataQuery(q) {
|
|
7
|
+
return typeof q === "string" && q.toLowerCase().startsWith(METADATA_SEARCH_PREFIX);
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
function setupSearch() {
|
|
6
11
|
["header-search", "sidebar-search"].forEach((id) => {
|
|
@@ -20,23 +25,55 @@ function setupSearch() {
|
|
|
20
25
|
searchQuery = "";
|
|
21
26
|
searchResults = null;
|
|
22
27
|
renderSidebar(allDocs);
|
|
28
|
+
if (window.refreshSearchInCurrentDoc) window.refreshSearchInCurrentDoc();
|
|
23
29
|
return;
|
|
24
30
|
}
|
|
25
31
|
searchQuery = q;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (_isMetadataQuery(q)) {
|
|
33
|
+
// Title/category can't match a metadata:// query — skip the local
|
|
34
|
+
// filter and wait for the server response.
|
|
35
|
+
searchResults = [];
|
|
36
|
+
renderSidebar([]);
|
|
37
|
+
} else {
|
|
38
|
+
// Immediate client-side filter for snappy UX
|
|
39
|
+
const local = allDocs.filter(
|
|
40
|
+
(d) =>
|
|
41
|
+
d.title.toLowerCase().includes(q.toLowerCase()) ||
|
|
42
|
+
d.category.toLowerCase().includes(q.toLowerCase()),
|
|
43
|
+
);
|
|
44
|
+
searchResults = local;
|
|
45
|
+
renderSidebar(local);
|
|
46
|
+
}
|
|
47
|
+
// Refresh highlights inside the currently open doc
|
|
48
|
+
if (window.refreshSearchInCurrentDoc) window.refreshSearchInCurrentDoc();
|
|
34
49
|
// Then full-text search from server
|
|
35
50
|
searchTimer = setTimeout(() => doSearch(q), 350);
|
|
36
51
|
});
|
|
37
52
|
});
|
|
38
53
|
}
|
|
39
54
|
|
|
55
|
+
// Programmatic trigger used by the Files modal after a replace/delete.
|
|
56
|
+
// Fills both search inputs and runs the server search immediately (no debounce).
|
|
57
|
+
function runSearchImmediate(q) {
|
|
58
|
+
const trimmed = (q || "").trim();
|
|
59
|
+
["header-search", "sidebar-search"].forEach((id) => {
|
|
60
|
+
const el = document.getElementById(id);
|
|
61
|
+
if (el) el.value = trimmed;
|
|
62
|
+
});
|
|
63
|
+
clearTimeout(searchTimer);
|
|
64
|
+
if (!trimmed) {
|
|
65
|
+
searchQuery = "";
|
|
66
|
+
searchResults = null;
|
|
67
|
+
renderSidebar(allDocs);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
searchQuery = trimmed;
|
|
71
|
+
searchResults = [];
|
|
72
|
+
renderSidebar([]);
|
|
73
|
+
doSearch(trimmed);
|
|
74
|
+
}
|
|
75
|
+
window.runSearchImmediate = runSearchImmediate;
|
|
76
|
+
|
|
40
77
|
async function doSearch(q) {
|
|
41
78
|
try {
|
|
42
79
|
const results = await fetch(
|