doc-survival-kit 1.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.
package/liens.js ADDED
@@ -0,0 +1,391 @@
1
+ // ── Données des liens ────────────────────────────────────────────────
2
+ const LIENS_KEY = "mes_liens";
3
+ var expandedCatTitres = (function () {
4
+ try { return JSON.parse(localStorage.getItem("expanded_cat_titres")) || {}; } catch (e) { return {}; }
5
+ })();
6
+
7
+ function loadLiens() {
8
+ try {
9
+ return JSON.parse(localStorage.getItem(LIENS_KEY)) || mesLiensDefaut;
10
+ } catch {
11
+ return mesLiensDefaut;
12
+ }
13
+ }
14
+
15
+ function saveLiens(data) {
16
+ localStorage.setItem(LIENS_KEY, JSON.stringify(data));
17
+ }
18
+
19
+ function checkDiff() {
20
+ const stored = localStorage.getItem(LIENS_KEY);
21
+ const areDifferent = stored !== JSON.stringify(mesLiensDefaut);
22
+ document.getElementById("btnSave").style.display = areDifferent
23
+ ? "inline-flex"
24
+ : "none";
25
+ }
26
+
27
+ // ── Persistance du FileSystemFileHandle via IndexedDB ────────────────
28
+ const IDB_NAME = "doc-survival-kit-db";
29
+ const IDB_STORE = "fileHandles";
30
+ const IDB_KEY = "mesLiens";
31
+
32
+ function ouvrirDB() {
33
+ return new Promise((resolve, reject) => {
34
+ const req = indexedDB.open(IDB_NAME, 1);
35
+ req.onupgradeneeded = (e) => e.target.result.createObjectStore(IDB_STORE);
36
+ req.onsuccess = (e) => resolve(e.target.result);
37
+ req.onerror = (e) => reject(e.target.error);
38
+ });
39
+ }
40
+
41
+ async function sauvegarderHandle(handle) {
42
+ const db = await ouvrirDB();
43
+ return new Promise((resolve, reject) => {
44
+ const tx = db.transaction(IDB_STORE, "readwrite");
45
+ tx.objectStore(IDB_STORE).put(handle, IDB_KEY);
46
+ tx.oncomplete = resolve;
47
+ tx.onerror = (e) => reject(e.target.error);
48
+ });
49
+ }
50
+
51
+ async function recupererHandle() {
52
+ const db = await ouvrirDB();
53
+ return new Promise((resolve, reject) => {
54
+ const tx = db.transaction(IDB_STORE, "readonly");
55
+ const req = tx.objectStore(IDB_STORE).get(IDB_KEY);
56
+ req.onsuccess = (e) => resolve(e.target.result || null);
57
+ req.onerror = (e) => reject(e.target.error);
58
+ });
59
+ }
60
+
61
+ // ── Enregistrement du fichier mesLiens.js ────────────────────────────
62
+ async function enregistrerModifications() {
63
+ if (!("showSaveFilePicker" in window)) {
64
+ console.error("File System Access API non disponible dans ce navigateur.");
65
+ return;
66
+ }
67
+
68
+ const handle = await recupererHandle();
69
+
70
+ if (!handle) {
71
+ document.getElementById("erreurFichierLiens").style.display = "none";
72
+ document.getElementById("modalPremiereSauvegarde").classList.add("open");
73
+ return;
74
+ }
75
+
76
+ await ecrireFichier(handle);
77
+ }
78
+
79
+ function fermerModalPremiereSauvegarde() {
80
+ document.getElementById("modalPremiereSauvegarde").classList.remove("open");
81
+ }
82
+
83
+ async function ouvrirSelecteurFichier() {
84
+ fermerModalPremiereSauvegarde();
85
+ try {
86
+ const dirHandle = await window.showDirectoryPicker();
87
+ const handle = await dirHandle.getFileHandle("mesLiens.js");
88
+ document.getElementById("erreurFichierLiens").style.display = "none";
89
+ await sauvegarderHandle(handle);
90
+ await ecrireFichier(handle);
91
+ } catch (err) {
92
+ if (err.name === "NotFoundError") {
93
+ document.getElementById("erreurFichierLiens").style.display = "block";
94
+ document.getElementById("modalPremiereSauvegarde").classList.add("open");
95
+ } else if (err.name !== "AbortError") {
96
+ console.error("Erreur lors de la sélection du dossier :", err);
97
+ }
98
+ }
99
+ }
100
+
101
+ async function ecrireFichier(handle) {
102
+ try {
103
+ let permission = await handle.queryPermission({ mode: "readwrite" });
104
+ if (permission !== "granted") {
105
+ permission = await handle.requestPermission({ mode: "readwrite" });
106
+ }
107
+ if (permission !== "granted") {
108
+ console.error("Permission d'écriture refusée.");
109
+ return;
110
+ }
111
+ const data = loadLiens();
112
+ const content =
113
+ "var mesLiensDefaut = " + JSON.stringify(data, null, 2) + ";\n";
114
+ const writable = await handle.createWritable();
115
+ await writable.write(content);
116
+ await writable.close();
117
+ mesLiensDefaut = data;
118
+ checkDiff();
119
+ console.log("mesLiens.js enregistré avec succès.", data);
120
+ } catch (err) {
121
+ console.error("Erreur lors de l'enregistrement :", err);
122
+ }
123
+ }
124
+
125
+ document
126
+ .getElementById("modalPremiereSauvegarde")
127
+ .addEventListener("click", function (e) {
128
+ if (e.target === this) fermerModalPremiereSauvegarde();
129
+ });
130
+
131
+ function toggleCat(titre) {
132
+ if (expandedCatTitres[titre]) {
133
+ delete expandedCatTitres[titre];
134
+ } else {
135
+ expandedCatTitres[titre] = true;
136
+ }
137
+ localStorage.setItem("expanded_cat_titres", JSON.stringify(expandedCatTitres));
138
+ renderLiens();
139
+ }
140
+
141
+ function toggleAllLiens() {
142
+ const data = loadLiens();
143
+ const allExpanded = data.length > 0 && data.every(function (c) { return expandedCatTitres[c.titre]; });
144
+ if (allExpanded) {
145
+ expandedCatTitres = {};
146
+ } else {
147
+ data.forEach(function (c) { expandedCatTitres[c.titre] = true; });
148
+ }
149
+ localStorage.setItem("expanded_cat_titres", JSON.stringify(expandedCatTitres));
150
+ renderLiens();
151
+ }
152
+
153
+ function updateToggleAllLiensBtn() {
154
+ const btn = document.getElementById("btnToggleAllLiens");
155
+ if (!btn) return;
156
+ const data = loadLiens();
157
+ const allExpanded = data.length > 0 && data.every(function (c) { return expandedCatTitres[c.titre]; });
158
+ btn.textContent = allExpanded ? window.t.liens_btn_collapse_all : window.t.liens_btn_expand_all;
159
+ }
160
+
161
+ function renderLiens() {
162
+ const data = loadLiens();
163
+ const container = document.getElementById("linksContainer");
164
+ container.innerHTML = data
165
+ .map(
166
+ (cat, idx) => {
167
+ const isCollapsed = !expandedCatTitres[cat.titre];
168
+ return `
169
+ <section class="${cat.theme}${isCollapsed ? " collapsed" : ""}">
170
+ <div class="cat-header">
171
+ <h2>${cat.titre}</h2>
172
+ <div class="cat-actions">
173
+ <button class="btn-add-link" onclick="ouvrirModalLien(${idx})" title="${window.t.liens_add_link_title}">+</button>
174
+ <button class="btn-del-cat" onclick="ouvrirConfirmSupprCat(${idx})" title="${window.t.liens_del_cat_title}">✕</button>
175
+ </div>
176
+ <button class="btn-toggle-cat" onclick="toggleCat('${cat.titre.replace(/'/g, "\\'")}')" title="${isCollapsed ? window.t.liens_btn_expand : window.t.liens_btn_collapse}">
177
+ <svg viewBox="0 0 10 6" width="10" height="6"><path d="M1 1 L5 5 L9 1" stroke="currentColor" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>
178
+ </button>
179
+ </div>
180
+ <div class="links">
181
+ ${cat.liens
182
+ .map(
183
+ (l, lidx) => `
184
+ <a href="${l.url}" target="_blank" class="link">
185
+ <div>
186
+ <span class="link-name">${l.nom}</span
187
+ ><span class="link-desc">— ${l.desc}</span>
188
+ </div>
189
+ <span class="arrow">↗</span>
190
+ <div class="link-actions">
191
+ <button class="btn-edit-link" onclick="ouvrirModalEditLien(event, ${idx}, ${lidx})" title="${window.t.liens_edit_link_title}">✎</button>
192
+ <button class="btn-del-link" onclick="supprimerLien(event, ${idx}, ${lidx})" title="${window.t.liens_del_link_title}">✕</button>
193
+ </div>
194
+ </a>`,
195
+ )
196
+ .join("")}
197
+ </div>
198
+ </section>`;
199
+ },
200
+ )
201
+ .join("");
202
+ checkDiff();
203
+ updateToggleAllLiensBtn();
204
+ }
205
+
206
+ if (!localStorage.getItem(LIENS_KEY)) {
207
+ saveLiens(mesLiensDefaut);
208
+ }
209
+
210
+ renderLiens();
211
+
212
+ // ── Ajout de catégorie ───────────────────────────────────────────────
213
+ function openModalCategorie() {
214
+ document.getElementById("catNom").value = "";
215
+ document.getElementById("catCouleur").value = "t-green";
216
+ document.getElementById("modalCategorie").classList.add("open");
217
+ document.getElementById("catNom").focus();
218
+ }
219
+
220
+ function closeModalCategorie() {
221
+ document.getElementById("modalCategorie").classList.remove("open");
222
+ }
223
+
224
+ function confirmerCategorie() {
225
+ const nom = document.getElementById("catNom").value.trim();
226
+ if (!nom) {
227
+ document.getElementById("catNom").focus();
228
+ return;
229
+ }
230
+ const theme = document.getElementById("catCouleur").value;
231
+ const data = loadLiens();
232
+ data.push({ theme, titre: nom, liens: [] });
233
+ saveLiens(data);
234
+ renderLiens();
235
+ closeModalCategorie();
236
+ }
237
+
238
+ document
239
+ .getElementById("modalCategorie")
240
+ .addEventListener("click", function (e) {
241
+ if (e.target === this) closeModalCategorie();
242
+ });
243
+
244
+ // ── Suppression de catégorie ─────────────────────────────────────────
245
+ let idxCatASupprimer = null;
246
+
247
+ function ouvrirConfirmSupprCat(idx) {
248
+ idxCatASupprimer = idx;
249
+ document.getElementById("modalConfirmSupprCat").classList.add("open");
250
+ }
251
+
252
+ function fermerConfirmSupprCat() {
253
+ idxCatASupprimer = null;
254
+ document.getElementById("modalConfirmSupprCat").classList.remove("open");
255
+ }
256
+
257
+ function supprimerCategorie() {
258
+ if (idxCatASupprimer === null) return;
259
+ const data = loadLiens();
260
+ data.splice(idxCatASupprimer, 1);
261
+ saveLiens(data);
262
+ renderLiens();
263
+ fermerConfirmSupprCat();
264
+ }
265
+
266
+ document
267
+ .getElementById("modalConfirmSupprCat")
268
+ .addEventListener("click", function (e) {
269
+ if (e.target === this) fermerConfirmSupprCat();
270
+ });
271
+
272
+ // ── Ajout d'un lien dans une catégorie ──────────────────────────────
273
+ let idxCatLien = null;
274
+
275
+ function ouvrirModalLien(idx) {
276
+ idxCatLien = idx;
277
+ document.getElementById("lienNom").value = "";
278
+ document.getElementById("lienDesc").value = "";
279
+ document.getElementById("lienUrl").value = "";
280
+ document.getElementById("modalLien").classList.add("open");
281
+ document.getElementById("lienNom").focus();
282
+ }
283
+
284
+ function fermerModalLien() {
285
+ idxCatLien = null;
286
+ document.getElementById("modalLien").classList.remove("open");
287
+ }
288
+
289
+ function confirmerLien() {
290
+ const nom = document.getElementById("lienNom").value.trim();
291
+ const desc = document.getElementById("lienDesc").value.trim();
292
+ const url = document.getElementById("lienUrl").value.trim();
293
+ if (!nom || !url) {
294
+ document.getElementById(!nom ? "lienNom" : "lienUrl").focus();
295
+ return;
296
+ }
297
+ const data = loadLiens();
298
+ data[idxCatLien].liens.push({ nom, desc, url });
299
+ saveLiens(data);
300
+ renderLiens();
301
+ fermerModalLien();
302
+ }
303
+
304
+ document.getElementById("modalLien").addEventListener("click", function (e) {
305
+ if (e.target === this) fermerModalLien();
306
+ });
307
+
308
+ // ── Suppression d'un lien individuel ────────────────────────────────
309
+ let supprLienCatIdx = null;
310
+ let supprLienIdx = null;
311
+
312
+ function supprimerLien(event, catIdx, lienIdx) {
313
+ event.preventDefault();
314
+ supprLienCatIdx = catIdx;
315
+ supprLienIdx = lienIdx;
316
+ document.getElementById("modalConfirmSupprLien").classList.add("open");
317
+ }
318
+
319
+ function fermerConfirmSupprLien() {
320
+ supprLienCatIdx = null;
321
+ supprLienIdx = null;
322
+ document.getElementById("modalConfirmSupprLien").classList.remove("open");
323
+ }
324
+
325
+ function confirmerSupprLien() {
326
+ if (supprLienCatIdx === null) return;
327
+ const data = loadLiens();
328
+ data[supprLienCatIdx].liens.splice(supprLienIdx, 1);
329
+ saveLiens(data);
330
+ renderLiens();
331
+ fermerConfirmSupprLien();
332
+ }
333
+
334
+ document
335
+ .getElementById("modalConfirmSupprLien")
336
+ .addEventListener("click", function (e) {
337
+ if (e.target === this) fermerConfirmSupprLien();
338
+ });
339
+
340
+ // ── Édition d'un lien ────────────────────────────────────────────────
341
+ let editLienCatIdx = null;
342
+ let editLienIdx = null;
343
+
344
+ function ouvrirModalEditLien(event, catIdx, lienIdx) {
345
+ event.preventDefault();
346
+ editLienCatIdx = catIdx;
347
+ editLienIdx = lienIdx;
348
+ const lien = loadLiens()[catIdx].liens[lienIdx];
349
+ document.getElementById("editLienNom").value = lien.nom;
350
+ document.getElementById("editLienDesc").value = lien.desc;
351
+ document.getElementById("editLienUrl").value = lien.url;
352
+ document.getElementById("modalEditLien").classList.add("open");
353
+ document.getElementById("editLienNom").focus();
354
+ }
355
+
356
+ function fermerModalEditLien() {
357
+ editLienCatIdx = null;
358
+ editLienIdx = null;
359
+ document.getElementById("modalEditLien").classList.remove("open");
360
+ }
361
+
362
+ function confirmerEditLien() {
363
+ const nom = document.getElementById("editLienNom").value.trim();
364
+ const desc = document.getElementById("editLienDesc").value.trim();
365
+ const url = document.getElementById("editLienUrl").value.trim();
366
+ if (!nom || !url) {
367
+ document.getElementById(!nom ? "editLienNom" : "editLienUrl").focus();
368
+ return;
369
+ }
370
+ const data = loadLiens();
371
+ data[editLienCatIdx].liens[editLienIdx] = { nom, desc, url };
372
+ saveLiens(data);
373
+ renderLiens();
374
+ fermerModalEditLien();
375
+ }
376
+
377
+ document
378
+ .getElementById("modalEditLien")
379
+ .addEventListener("click", function (e) {
380
+ if (e.target === this) fermerModalEditLien();
381
+ });
382
+
383
+ // ── Mode édition ─────────────────────────────────────────────────────
384
+ function toggleEditMode() {
385
+ const container = document.getElementById("linksContainer");
386
+ const btn = document.getElementById("btnEditMode");
387
+ const actif = container.classList.toggle("edit-mode");
388
+ btn.textContent = actif
389
+ ? window.t.liens_btn_quit_edit_mode
390
+ : window.t.liens_btn_edit_mode;
391
+ }
package/mesLiens.js ADDED
@@ -0,0 +1,66 @@
1
+ var mesLiensDefaut = [
2
+ {
3
+ theme: "t-green",
4
+ titre: "Dev & Code",
5
+ liens: [
6
+ {
7
+ nom: "Tailwind CSS",
8
+ desc: "Documentation officielle",
9
+ url: "https://tailwindcss.com/docs",
10
+ },
11
+ {
12
+ nom: "MDN Web Docs",
13
+ desc: "Référence HTML, CSS, JS",
14
+ url: "https://developer.mozilla.org/fr/",
15
+ },
16
+ {
17
+ nom: "GitHub",
18
+ desc: "Repos, issues, PRs",
19
+ url: "https://github.com",
20
+ },
21
+ ],
22
+ },
23
+ {
24
+ theme: "t-violet",
25
+ titre: "Design & Inspi",
26
+ liens: [
27
+ {
28
+ nom: "Unsplash",
29
+ desc: "Photos libres de droits",
30
+ url: "https://unsplash.com",
31
+ },
32
+ {
33
+ nom: "Dribbble",
34
+ desc: "Inspiration UI/UX",
35
+ url: "https://dribbble.com",
36
+ },
37
+ ],
38
+ },
39
+ {
40
+ theme: "t-amber",
41
+ titre: "Outils",
42
+ liens: [
43
+ {
44
+ nom: "Excalidraw",
45
+ desc: "Whiteboard hand-drawn",
46
+ url: "https://excalidraw.com",
47
+ },
48
+ {
49
+ nom: "Notion",
50
+ desc: "Notes et docs tout-en-un",
51
+ url: "https://notion.so",
52
+ },
53
+ ],
54
+ },
55
+ {
56
+ theme: "t-sky",
57
+ titre: "Veille",
58
+ liens: [
59
+ {
60
+ nom: "Hacker News",
61
+ desc: "Actu tech et startups",
62
+ url: "https://news.ycombinator.com",
63
+ },
64
+ ],
65
+ },
66
+ ];
package/mesNotes.js ADDED
@@ -0,0 +1,90 @@
1
+ var mesNotesDefaut = [
2
+ {
3
+ "id": 1700000000001,
4
+ "theme": "t-green",
5
+ "titre": "Commandes Git",
6
+ "blocs": [
7
+ { "type": "b", "content": "Annuler le dernier commit (garder les modifs)" },
8
+ { "type": "pre", "content": "git reset --soft HEAD~1" },
9
+ { "type": "b", "content": "Stash — mettre de côté" },
10
+ { "type": "pre", "content": "git stash\ngit stash pop" },
11
+ { "type": "b", "content": "Rebaser sur main" },
12
+ { "type": "pre", "content": "git fetch origin\ngit rebase origin/main" },
13
+ { "type": "b", "content": "Voir les branches distantes" },
14
+ { "type": "pre", "content": "git branch -r" }
15
+ ]
16
+ },
17
+ {
18
+ "id": 1700000000002,
19
+ "theme": "t-sky",
20
+ "titre": "Raccourcis VS Code",
21
+ "blocs": [
22
+ { "type": "b", "content": "Édition" },
23
+ {
24
+ "type": "ul",
25
+ "items": [
26
+ "Ctrl+D — sélectionner le mot suivant",
27
+ "Alt+↑/↓ — déplacer la ligne",
28
+ "Ctrl+Shift+K — supprimer la ligne",
29
+ "Ctrl+/ — commenter / décommenter"
30
+ ]
31
+ },
32
+ { "type": "b", "content": "Navigation" },
33
+ {
34
+ "type": "ul",
35
+ "items": [
36
+ "Ctrl+P — ouvrir un fichier",
37
+ "Ctrl+Shift+F — recherche globale",
38
+ "Ctrl+` — ouvrir le terminal",
39
+ "Ctrl+B — afficher/masquer l'explorateur"
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "id": 1700000000003,
46
+ "theme": "t-amber",
47
+ "titre": "Regex utiles",
48
+ "blocs": [
49
+ {
50
+ "type": "table",
51
+ "headers": ["Sujet", "Expression"],
52
+ "rows": [
53
+ ["Email", "^[\\w.-]+@[\\w.-]+\\.[a-z]{2,}$"],
54
+ ["URL", "https?:\\/\\/[\\w\\-._~:/?#\\[\\]@!$&'()*+,;=%]+"],
55
+ ["Date JJ/MM/AAAA", "^(0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/\\d{4}$"]
56
+ ]
57
+ },
58
+ {
59
+ "type": "table",
60
+ "headers": ["Flag", "Description"],
61
+ "rows": [
62
+ ["g", "global — toutes les occurrences"],
63
+ ["i", "insensible à la casse"],
64
+ ["m", "multiligne (^ et $ par ligne)"]
65
+ ]
66
+ }
67
+ ]
68
+ },
69
+ {
70
+ "id": 1700000000004,
71
+ "theme": "t-violet",
72
+ "titre": "Comment utiliser mesNotes",
73
+ "blocs": [
74
+ { "type": "p", "content": "Chaque note est composée de blocs que tu empiles librement." },
75
+ { "type": "b", "content": "Types de blocs disponibles" },
76
+ {
77
+ "type": "ul",
78
+ "items": [
79
+ "Titre — texte court mis en gras",
80
+ "Texte — paragraphe libre",
81
+ "Code — bloc monospace à fond gris",
82
+ "Liste — bullet points, un élément par ligne",
83
+ "Tableau — lignes séparées par |, 1re ligne = en-têtes"
84
+ ]
85
+ },
86
+ { "type": "b", "content": "Pour modifier" },
87
+ { "type": "p", "content": "Active le mode édition pour ajouter, modifier ou supprimer des blocs et des notes. N'oublie pas d'enregistrer les modifications dans le fichier." }
88
+ ]
89
+ }
90
+ ];