living-documentation 3.7.0 → 4.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.
Potentially problematic release.
This version of living-documentation might be problematic. Click here for more details.
- package/README.md +2 -0
- package/dist/src/frontend/diagram/clipboard.js +80 -1
- package/dist/src/frontend/diagram/constants.js +1 -0
- package/dist/src/frontend/diagram/groups.js +99 -0
- package/dist/src/frontend/diagram/image-upload.js +34 -0
- package/dist/src/frontend/diagram/link-panel.js +138 -0
- package/dist/src/frontend/diagram/main.js +51 -6
- package/dist/src/frontend/diagram/network.js +107 -7
- package/dist/src/frontend/diagram/node-panel.js +16 -0
- package/dist/src/frontend/diagram/node-rendering.js +130 -0
- package/dist/src/frontend/diagram/persistence.js +7 -1
- package/dist/src/frontend/diagram/toast.js +21 -0
- package/dist/src/frontend/diagram.html +177 -22
- package/dist/src/frontend/index.html +75 -239
- package/dist/src/frontend/wordcloud.js +321 -0
- package/dist/src/routes/wordcloud.d.ts +3 -0
- package/dist/src/routes/wordcloud.d.ts.map +1 -0
- package/dist/src/routes/wordcloud.js +73 -0
- package/dist/src/routes/wordcloud.js.map +1 -0
- package/dist/src/server.d.ts.map +1 -1
- package/dist/src/server.js +2 -0
- package/dist/src/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -210,6 +210,47 @@
|
|
|
210
210
|
}
|
|
211
211
|
#rh-label-rotate:active { cursor: grabbing; }
|
|
212
212
|
.dark #rh-label-rotate { background: #374151; }
|
|
213
|
+
|
|
214
|
+
/* Toast notifications */
|
|
215
|
+
#toastContainer {
|
|
216
|
+
position: fixed;
|
|
217
|
+
bottom: 1.5rem;
|
|
218
|
+
right: 1.5rem;
|
|
219
|
+
display: flex;
|
|
220
|
+
flex-direction: column-reverse;
|
|
221
|
+
gap: 0.5rem;
|
|
222
|
+
z-index: 1000;
|
|
223
|
+
pointer-events: none;
|
|
224
|
+
}
|
|
225
|
+
.ld-toast {
|
|
226
|
+
pointer-events: all;
|
|
227
|
+
padding: 0.6rem 1rem;
|
|
228
|
+
border-radius: 0.5rem;
|
|
229
|
+
font-size: 0.8rem;
|
|
230
|
+
font-weight: 500;
|
|
231
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
opacity: 0;
|
|
234
|
+
transform: translateY(0.5rem);
|
|
235
|
+
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
236
|
+
background: #1c1917;
|
|
237
|
+
color: #fafaf9;
|
|
238
|
+
border: 1px solid #292524;
|
|
239
|
+
}
|
|
240
|
+
.dark .ld-toast {
|
|
241
|
+
background: #fafaf9;
|
|
242
|
+
color: #1c1917;
|
|
243
|
+
border-color: #e7e5e4;
|
|
244
|
+
}
|
|
245
|
+
.ld-toast--error {
|
|
246
|
+
background: #7f1d1d;
|
|
247
|
+
color: #fef2f2;
|
|
248
|
+
border-color: #991b1b;
|
|
249
|
+
}
|
|
250
|
+
.ld-toast--visible {
|
|
251
|
+
opacity: 1;
|
|
252
|
+
transform: translateY(0);
|
|
253
|
+
}
|
|
213
254
|
</style>
|
|
214
255
|
</head>
|
|
215
256
|
<body
|
|
@@ -224,10 +265,10 @@
|
|
|
224
265
|
<header
|
|
225
266
|
class="flex items-center gap-1 px-2 h-12 shrink-0 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm z-10"
|
|
226
267
|
>
|
|
227
|
-
<
|
|
228
|
-
|
|
268
|
+
<button
|
|
269
|
+
onclick="history.back()"
|
|
229
270
|
class="tool-btn !w-auto px-2 text-xs font-medium text-gray-500 dark:text-gray-400"
|
|
230
|
-
>←
|
|
271
|
+
>← Back</button
|
|
231
272
|
>
|
|
232
273
|
<div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
|
|
233
274
|
<button id="btnSidebar" class="tool-btn" title="Mes diagrammes">
|
|
@@ -358,6 +399,17 @@
|
|
|
358
399
|
>
|
|
359
400
|
T
|
|
360
401
|
</button>
|
|
402
|
+
<button
|
|
403
|
+
id="toolImage"
|
|
404
|
+
class="tool-btn"
|
|
405
|
+
title="Image — double-clic sur le canvas pour choisir un fichier, ou coller (⌘V) depuis le presse-papier"
|
|
406
|
+
>
|
|
407
|
+
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
408
|
+
<rect x="1" y="1" width="12" height="12" rx="1.5"/>
|
|
409
|
+
<circle cx="4.5" cy="4.5" r="1.2"/>
|
|
410
|
+
<path d="M1 9.5 L4 6.5 L6.5 9 L9 7 L13 10.5"/>
|
|
411
|
+
</svg>
|
|
412
|
+
</button>
|
|
361
413
|
<div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-0.5"></div>
|
|
362
414
|
|
|
363
415
|
<button
|
|
@@ -538,7 +590,14 @@
|
|
|
538
590
|
style="bottom: -5px; right: -5px; cursor: se-resize"
|
|
539
591
|
></div>
|
|
540
592
|
<div id="rh-rotate" title="Rotation forme">↻</div>
|
|
541
|
-
<div id="rh-label-rotate" title="Rotation texte" style="left: 0; top: -28px;">
|
|
593
|
+
<div id="rh-label-rotate" title="Rotation texte" style="left: 0; top: -28px;">
|
|
594
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
595
|
+
<g transform="rotate(25,5,5)">
|
|
596
|
+
<line x1="2.5" y1="3" x2="7.5" y2="3"/>
|
|
597
|
+
<line x1="5" y1="3" x2="5" y2="8"/>
|
|
598
|
+
</g>
|
|
599
|
+
</svg>
|
|
600
|
+
</div>
|
|
542
601
|
</div>
|
|
543
602
|
|
|
544
603
|
<!-- Node panel -->
|
|
@@ -626,6 +685,16 @@
|
|
|
626
685
|
>
|
|
627
686
|
✎
|
|
628
687
|
</button>
|
|
688
|
+
<button
|
|
689
|
+
id="btnNodeLink"
|
|
690
|
+
class="tool-btn !w-6 !h-6"
|
|
691
|
+
title="Ajouter / modifier un lien"
|
|
692
|
+
>
|
|
693
|
+
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
694
|
+
<path d="M5 7.5a3.5 3.5 0 0 0 5 0l1.5-1.5a3.5 3.5 0 0 0-5-5L5 2.5"/>
|
|
695
|
+
<path d="M8 5.5a3.5 3.5 0 0 0-5 0L1.5 7a3.5 3.5 0 0 0 5 5L8 10.5"/>
|
|
696
|
+
</svg>
|
|
697
|
+
</button>
|
|
629
698
|
<div class="panel-sep"></div>
|
|
630
699
|
<button
|
|
631
700
|
id="btnNodeFontDecrease"
|
|
@@ -814,40 +883,125 @@
|
|
|
814
883
|
|
|
815
884
|
<div class="panel-sep"></div>
|
|
816
885
|
|
|
817
|
-
<!-- Stamp: copy color -->
|
|
886
|
+
<!-- Stamp: copy color (goutte) -->
|
|
818
887
|
<button
|
|
819
888
|
id="btnStampColor"
|
|
820
889
|
class="tool-btn !w-7 !h-6"
|
|
821
890
|
title="Tampon couleur — sélectionner les cibles, cliquer ici, puis cliquer la source"
|
|
822
891
|
>
|
|
823
892
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
824
|
-
<
|
|
825
|
-
<rect x="7" y="7" width="6" height="6" rx="1" fill="currentColor" stroke="none" opacity="0.5"/>
|
|
826
|
-
<path d="M7 3.5h4M3.5 7v4"/>
|
|
827
|
-
<circle cx="10.5" cy="10.5" r="1" fill="currentColor" stroke="none"/>
|
|
893
|
+
<path d="M7 2 C7 2 3 6.5 3 9 a4 4 0 0 0 8 0 C11 6.5 7 2 7 2 Z" fill="currentColor" fill-opacity="0.25"/>
|
|
828
894
|
</svg>
|
|
829
895
|
</button>
|
|
830
896
|
|
|
831
|
-
<!-- Stamp: copy
|
|
897
|
+
<!-- Stamp: copy font size -->
|
|
898
|
+
<button
|
|
899
|
+
id="btnStampFontSize"
|
|
900
|
+
class="tool-btn !w-7 !h-6 font-mono text-xs font-bold"
|
|
901
|
+
title="Tampon taille police — sélectionner les cibles, cliquer ici, puis cliquer la source"
|
|
902
|
+
>Aa</button>
|
|
903
|
+
|
|
904
|
+
<div class="panel-sep"></div>
|
|
905
|
+
|
|
906
|
+
<!-- Rotation anti-horaire 10° -->
|
|
907
|
+
<button
|
|
908
|
+
id="btnRotateCCW"
|
|
909
|
+
class="tool-btn !w-7 !h-6"
|
|
910
|
+
title="Rotation anti-horaire 10°"
|
|
911
|
+
style="font-size:15px; line-height:1;"
|
|
912
|
+
>↺</button>
|
|
913
|
+
|
|
914
|
+
<!-- Rotation horaire 10° -->
|
|
832
915
|
<button
|
|
833
|
-
id="
|
|
916
|
+
id="btnRotateCW"
|
|
834
917
|
class="tool-btn !w-7 !h-6"
|
|
835
|
-
title="
|
|
918
|
+
title="Rotation horaire 10°"
|
|
919
|
+
style="font-size:15px; line-height:1;"
|
|
920
|
+
>↻</button>
|
|
921
|
+
|
|
922
|
+
<div class="panel-sep"></div>
|
|
923
|
+
|
|
924
|
+
<!-- Copy as PNG -->
|
|
925
|
+
<button
|
|
926
|
+
id="btnCopyPng"
|
|
927
|
+
class="tool-btn !h-6 px-1.5 font-mono text-xs font-semibold flex items-center gap-0.5"
|
|
928
|
+
title="Copier la sélection en PNG (⌘⇧C)"
|
|
836
929
|
>
|
|
837
|
-
<svg width="
|
|
838
|
-
<
|
|
839
|
-
<polyline points="
|
|
840
|
-
<line x1="
|
|
841
|
-
<line x1="7" y1="7" x2="9" y2="7" stroke-width="1.8"/>
|
|
930
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">
|
|
931
|
+
<line x1="5" y1="7" x2="5" y2="1"/>
|
|
932
|
+
<polyline points="2,4 5,1 8,4"/>
|
|
933
|
+
<line x1="1" y1="9" x2="9" y2="9"/>
|
|
842
934
|
</svg>
|
|
935
|
+
PNG
|
|
843
936
|
</button>
|
|
844
937
|
|
|
845
|
-
|
|
938
|
+
<div class="panel-sep"></div>
|
|
939
|
+
|
|
940
|
+
<!-- Group -->
|
|
846
941
|
<button
|
|
847
|
-
id="
|
|
848
|
-
class="tool-btn !w-
|
|
849
|
-
title="
|
|
850
|
-
>
|
|
942
|
+
id="btnGroup"
|
|
943
|
+
class="tool-btn !w-8 !h-7"
|
|
944
|
+
title="Grouper la sélection"
|
|
945
|
+
>
|
|
946
|
+
<svg width="22" height="14" viewBox="0 0 22 14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
|
|
947
|
+
<rect x="0.7" y="1" width="20.6" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
|
|
948
|
+
<rect x="4" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
|
|
949
|
+
<rect x="15" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
|
|
950
|
+
</svg>
|
|
951
|
+
</button>
|
|
952
|
+
|
|
953
|
+
<!-- Ungroup -->
|
|
954
|
+
<button
|
|
955
|
+
id="btnUngroup"
|
|
956
|
+
class="tool-btn !w-8 !h-7"
|
|
957
|
+
title="Dégrouper"
|
|
958
|
+
>
|
|
959
|
+
<svg width="26" height="14" viewBox="0 0 26 14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round">
|
|
960
|
+
<rect x="0.7" y="1" width="9" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
|
|
961
|
+
<rect x="3" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
|
|
962
|
+
<rect x="16.3" y="1" width="9" height="12" rx="2" stroke-dasharray="2.5,1.5"/>
|
|
963
|
+
<rect x="19" y="4.5" width="3" height="5" rx="1" fill="currentColor"/>
|
|
964
|
+
</svg>
|
|
965
|
+
</button>
|
|
966
|
+
</div>
|
|
967
|
+
|
|
968
|
+
<!-- Link panel -->
|
|
969
|
+
<div id="linkPanel" class="float-panel hidden" style="min-width:260px; flex-direction:column; align-items:stretch; gap:0.5rem; padding:0.75rem;">
|
|
970
|
+
<div class="text-xs font-semibold text-gray-500 dark:text-gray-400 mb-1">Lien sur cette forme</div>
|
|
971
|
+
|
|
972
|
+
<label class="flex items-center gap-2 text-xs cursor-pointer">
|
|
973
|
+
<input type="radio" name="linkType" id="linkTypeUrl" value="url" class="accent-orange-500"/>
|
|
974
|
+
URL externe
|
|
975
|
+
</label>
|
|
976
|
+
<div id="linkUrlRow">
|
|
977
|
+
<input id="linkUrlInput" type="url" placeholder="https://…"
|
|
978
|
+
class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400"/>
|
|
979
|
+
</div>
|
|
980
|
+
|
|
981
|
+
<label class="flex items-center gap-2 text-xs cursor-pointer">
|
|
982
|
+
<input type="radio" name="linkType" id="linkTypeDiagram" value="diagram" class="accent-orange-500"/>
|
|
983
|
+
Diagramme existant
|
|
984
|
+
</label>
|
|
985
|
+
<div id="linkDiagramRow" class="hidden">
|
|
986
|
+
<select id="linkDiagramSelect"
|
|
987
|
+
class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400">
|
|
988
|
+
</select>
|
|
989
|
+
</div>
|
|
990
|
+
|
|
991
|
+
<label class="flex items-center gap-2 text-xs cursor-pointer">
|
|
992
|
+
<input type="radio" name="linkType" id="linkTypeNew" value="new" class="accent-orange-500"/>
|
|
993
|
+
Nouveau diagramme
|
|
994
|
+
</label>
|
|
995
|
+
<div id="linkNewRow" class="hidden">
|
|
996
|
+
<input id="linkNewName" type="text" placeholder="Nom du diagramme…"
|
|
997
|
+
class="w-full text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-1 focus:ring-orange-400"/>
|
|
998
|
+
</div>
|
|
999
|
+
|
|
1000
|
+
<div class="flex gap-1 mt-1">
|
|
1001
|
+
<button id="btnLinkSave" class="flex-1 text-xs bg-orange-500 hover:bg-orange-600 text-white rounded px-2 py-1">Enregistrer</button>
|
|
1002
|
+
<button id="btnLinkRemove" class="text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 hover:bg-red-50 dark:hover:bg-red-900/20 hover:text-red-600">Retirer</button>
|
|
1003
|
+
<button id="btnLinkCancel" class="text-xs border border-gray-300 dark:border-gray-600 rounded px-2 py-1 hover:bg-gray-100 dark:hover:bg-gray-700">✕</button>
|
|
1004
|
+
</div>
|
|
851
1005
|
</div>
|
|
852
1006
|
|
|
853
1007
|
<!-- Edge panel -->
|
|
@@ -969,6 +1123,7 @@
|
|
|
969
1123
|
</div>
|
|
970
1124
|
</div>
|
|
971
1125
|
|
|
1126
|
+
<div id="toastContainer"></div>
|
|
972
1127
|
<script type="module" src="/diagram/main.js"></script>
|
|
973
1128
|
</body>
|
|
974
1129
|
</html>
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
<!-- WordCloud2.js — vendored from npm, no CDN dependency -->
|
|
19
19
|
<script src="/vendor/wordcloud2.js"></script>
|
|
20
|
+
<!-- Word Cloud logic — stop words, browser, rendering -->
|
|
21
|
+
<script src="/wordcloud.js"></script>
|
|
20
22
|
|
|
21
23
|
<script>
|
|
22
24
|
tailwind.config = {
|
|
@@ -281,7 +283,7 @@
|
|
|
281
283
|
<article id="doc-view" class="hidden max-w-4xl mx-auto px-6 py-8">
|
|
282
284
|
<!-- Doc header -->
|
|
283
285
|
<header
|
|
284
|
-
class="mb-8 pb-6 border-b border-gray-
|
|
286
|
+
class="sticky top-0 z-10 bg-gray-50 dark:bg-gray-950 -mx-6 px-6 pt-8 mb-8 pb-6 border-b border-gray-300 dark:border-gray-800"
|
|
285
287
|
>
|
|
286
288
|
<div class="flex items-start justify-between gap-4 flex-wrap">
|
|
287
289
|
<div class="flex-1 min-w-0">
|
|
@@ -391,6 +393,73 @@
|
|
|
391
393
|
✕ Close
|
|
392
394
|
</button>
|
|
393
395
|
</div>
|
|
396
|
+
<!-- Search root toolbar -->
|
|
397
|
+
<div class="border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shrink-0">
|
|
398
|
+
<!-- Row 1: path + browse + launch -->
|
|
399
|
+
<div class="flex items-center gap-3 px-6 py-3">
|
|
400
|
+
<label class="text-xs font-medium text-gray-500 dark:text-gray-400 shrink-0">Search root</label>
|
|
401
|
+
<input
|
|
402
|
+
id="wc-root"
|
|
403
|
+
type="text"
|
|
404
|
+
readonly
|
|
405
|
+
spellcheck="false"
|
|
406
|
+
class="flex-1 px-3 py-1.5 text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-gray-100 font-mono cursor-default focus:outline-none"
|
|
407
|
+
/>
|
|
408
|
+
<button
|
|
409
|
+
onclick="wcToggleBrowser()"
|
|
410
|
+
class="text-sm px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors shrink-0"
|
|
411
|
+
>
|
|
412
|
+
📁 Browse
|
|
413
|
+
</button>
|
|
414
|
+
<button
|
|
415
|
+
onclick="launchWordCloud()"
|
|
416
|
+
class="text-sm px-4 py-1.5 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-semibold transition-colors shrink-0"
|
|
417
|
+
>
|
|
418
|
+
▶ Launch
|
|
419
|
+
</button>
|
|
420
|
+
</div>
|
|
421
|
+
<!-- Row 2: extension checkboxes -->
|
|
422
|
+
<div class="flex items-center gap-x-4 gap-y-2 px-6 pb-3 flex-wrap">
|
|
423
|
+
<span class="text-xs font-medium text-gray-500 dark:text-gray-400 shrink-0">Extensions</span>
|
|
424
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="md" checked /> .md</label>
|
|
425
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="txt" /> .txt</label>
|
|
426
|
+
<span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
|
|
427
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="ts" /> .ts</label>
|
|
428
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="tsx" /> .tsx</label>
|
|
429
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="js" /> .js</label>
|
|
430
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="jsx" /> .jsx</label>
|
|
431
|
+
<span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
|
|
432
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="java" /> .java</label>
|
|
433
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="kt" /> .kt</label>
|
|
434
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="py" /> .py</label>
|
|
435
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="go" /> .go</label>
|
|
436
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="rs" /> .rs</label>
|
|
437
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="cs" /> .cs</label>
|
|
438
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="swift" /> .swift</label>
|
|
439
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="rb" /> .rb</label>
|
|
440
|
+
<span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
|
|
441
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="html" /> .html</label>
|
|
442
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="css" /> .css</label>
|
|
443
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="scss" /> .scss</label>
|
|
444
|
+
<span class="text-gray-200 dark:text-gray-700 text-xs">|</span>
|
|
445
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="yml" /> .yml</label>
|
|
446
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="yaml" /> .yaml</label>
|
|
447
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="json" /> .json</label>
|
|
448
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="xml" /> .xml</label>
|
|
449
|
+
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300 cursor-pointer"><input type="checkbox" class="wc-ext" value="toml" /> .toml</label>
|
|
450
|
+
</div>
|
|
451
|
+
<!-- Row 3: inline folder browser (collapsed by default) -->
|
|
452
|
+
<div id="wc-browser" class="hidden border-t border-gray-200 dark:border-gray-700">
|
|
453
|
+
<div class="flex items-center gap-2 px-3 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
|
454
|
+
<button id="wc-browse-up" onclick="wcBrowseUp()"
|
|
455
|
+
class="text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-30 disabled:pointer-events-none shrink-0">
|
|
456
|
+
↑ Up
|
|
457
|
+
</button>
|
|
458
|
+
<span id="wc-browse-path" class="font-mono text-xs text-gray-400 dark:text-gray-500 truncate flex-1 text-right"></span>
|
|
459
|
+
</div>
|
|
460
|
+
<div id="wc-browse-list" class="divide-y divide-gray-100 dark:divide-gray-800 max-h-52 overflow-y-auto"></div>
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
394
463
|
<div
|
|
395
464
|
id="wc-body"
|
|
396
465
|
class="flex-1 relative overflow-hidden flex items-center justify-center"
|
|
@@ -419,6 +488,7 @@
|
|
|
419
488
|
applyDarkMode(loadDarkPref());
|
|
420
489
|
setupDarkToggle();
|
|
421
490
|
setupSearch();
|
|
491
|
+
wcRestorePrefs();
|
|
422
492
|
await loadConfig();
|
|
423
493
|
await loadDocuments();
|
|
424
494
|
|
|
@@ -685,7 +755,10 @@
|
|
|
685
755
|
}
|
|
686
756
|
|
|
687
757
|
// ── Edit mode ──────────────────────────────────────────────
|
|
758
|
+
let _editScrollTop = 0;
|
|
759
|
+
|
|
688
760
|
function enterEditMode() {
|
|
761
|
+
_editScrollTop = document.getElementById("content-area").scrollTop;
|
|
689
762
|
const editor = document.getElementById("doc-editor");
|
|
690
763
|
editor.value = currentDocContent;
|
|
691
764
|
document.getElementById("doc-content").classList.add("hidden");
|
|
@@ -704,6 +777,7 @@
|
|
|
704
777
|
document.getElementById("edit-actions").classList.add("hidden");
|
|
705
778
|
document.getElementById("view-actions").classList.remove("hidden");
|
|
706
779
|
document.getElementById("edit-save-msg").textContent = "";
|
|
780
|
+
document.getElementById("content-area").scrollTop = _editScrollTop;
|
|
707
781
|
}
|
|
708
782
|
|
|
709
783
|
async function handleEditorPaste(e) {
|
|
@@ -837,244 +911,6 @@
|
|
|
837
911
|
.replace(/'/g, "'");
|
|
838
912
|
}
|
|
839
913
|
|
|
840
|
-
// ── Word Cloud ─────────────────────────────────────────────
|
|
841
|
-
const WC_STOP_WORDS = new Set([
|
|
842
|
-
// English
|
|
843
|
-
"the",
|
|
844
|
-
"and",
|
|
845
|
-
"for",
|
|
846
|
-
"are",
|
|
847
|
-
"but",
|
|
848
|
-
"not",
|
|
849
|
-
"you",
|
|
850
|
-
"all",
|
|
851
|
-
"this",
|
|
852
|
-
"that",
|
|
853
|
-
"with",
|
|
854
|
-
"have",
|
|
855
|
-
"from",
|
|
856
|
-
"they",
|
|
857
|
-
"will",
|
|
858
|
-
"one",
|
|
859
|
-
"been",
|
|
860
|
-
"can",
|
|
861
|
-
"has",
|
|
862
|
-
"was",
|
|
863
|
-
"more",
|
|
864
|
-
"also",
|
|
865
|
-
"when",
|
|
866
|
-
"there",
|
|
867
|
-
"their",
|
|
868
|
-
"what",
|
|
869
|
-
"about",
|
|
870
|
-
"which",
|
|
871
|
-
"would",
|
|
872
|
-
"into",
|
|
873
|
-
"than",
|
|
874
|
-
"then",
|
|
875
|
-
"each",
|
|
876
|
-
"just",
|
|
877
|
-
"over",
|
|
878
|
-
"after",
|
|
879
|
-
"such",
|
|
880
|
-
"here",
|
|
881
|
-
"its",
|
|
882
|
-
"your",
|
|
883
|
-
"our",
|
|
884
|
-
"some",
|
|
885
|
-
"were",
|
|
886
|
-
"very",
|
|
887
|
-
"only",
|
|
888
|
-
"out",
|
|
889
|
-
"had",
|
|
890
|
-
"she",
|
|
891
|
-
"his",
|
|
892
|
-
"her",
|
|
893
|
-
"him",
|
|
894
|
-
"who",
|
|
895
|
-
"how",
|
|
896
|
-
"any",
|
|
897
|
-
"other",
|
|
898
|
-
"these",
|
|
899
|
-
"those",
|
|
900
|
-
"being",
|
|
901
|
-
"may",
|
|
902
|
-
"use",
|
|
903
|
-
"used",
|
|
904
|
-
"using",
|
|
905
|
-
"should",
|
|
906
|
-
"could",
|
|
907
|
-
"would",
|
|
908
|
-
"shall",
|
|
909
|
-
"must",
|
|
910
|
-
"need",
|
|
911
|
-
"via",
|
|
912
|
-
"per",
|
|
913
|
-
"like",
|
|
914
|
-
// French
|
|
915
|
-
"les",
|
|
916
|
-
"des",
|
|
917
|
-
"une",
|
|
918
|
-
"pour",
|
|
919
|
-
"pas",
|
|
920
|
-
"sur",
|
|
921
|
-
"par",
|
|
922
|
-
"est",
|
|
923
|
-
"qui",
|
|
924
|
-
"que",
|
|
925
|
-
"dans",
|
|
926
|
-
"avec",
|
|
927
|
-
"sont",
|
|
928
|
-
"plus",
|
|
929
|
-
"tout",
|
|
930
|
-
"aux",
|
|
931
|
-
"mais",
|
|
932
|
-
"comme",
|
|
933
|
-
"vous",
|
|
934
|
-
"nous",
|
|
935
|
-
"leur",
|
|
936
|
-
"lui",
|
|
937
|
-
"elle",
|
|
938
|
-
"ils",
|
|
939
|
-
"elles",
|
|
940
|
-
"ces",
|
|
941
|
-
"ses",
|
|
942
|
-
"mon",
|
|
943
|
-
"ton",
|
|
944
|
-
"son",
|
|
945
|
-
"mes",
|
|
946
|
-
"tes",
|
|
947
|
-
"ainsi",
|
|
948
|
-
"donc",
|
|
949
|
-
"alors",
|
|
950
|
-
"car",
|
|
951
|
-
"peut",
|
|
952
|
-
"fait",
|
|
953
|
-
"encore",
|
|
954
|
-
"bien",
|
|
955
|
-
"aussi",
|
|
956
|
-
"très",
|
|
957
|
-
"même",
|
|
958
|
-
"entre",
|
|
959
|
-
"vers",
|
|
960
|
-
"dont",
|
|
961
|
-
"sans",
|
|
962
|
-
"sous",
|
|
963
|
-
]);
|
|
964
|
-
|
|
965
|
-
async function openWordCloud() {
|
|
966
|
-
const overlay = document.getElementById("wc-overlay");
|
|
967
|
-
const status = document.getElementById("wc-status");
|
|
968
|
-
const canvas = document.getElementById("wc-canvas");
|
|
969
|
-
overlay.classList.remove("hidden");
|
|
970
|
-
status.textContent = "Loading documents…";
|
|
971
|
-
status.classList.remove("hidden");
|
|
972
|
-
canvas.classList.add("hidden");
|
|
973
|
-
|
|
974
|
-
try {
|
|
975
|
-
const docs = allDocs.length
|
|
976
|
-
? allDocs
|
|
977
|
-
: await fetch("/api/documents").then((r) => r.json());
|
|
978
|
-
status.textContent = `Analyzing ${docs.length} documents…`;
|
|
979
|
-
|
|
980
|
-
const results = await Promise.all(
|
|
981
|
-
docs.map((doc) =>
|
|
982
|
-
fetch("/api/documents/" + doc.id)
|
|
983
|
-
.then((r) => (r.ok ? r.json() : null))
|
|
984
|
-
.catch(() => null),
|
|
985
|
-
),
|
|
986
|
-
);
|
|
987
|
-
|
|
988
|
-
const freq = {};
|
|
989
|
-
for (const doc of results) {
|
|
990
|
-
console.log("doc = ", doc);
|
|
991
|
-
if (!doc?.content) continue;
|
|
992
|
-
for (const w of extractWordsFromMarkdown(doc.content)) {
|
|
993
|
-
freq[w] = (freq[w] || 0) + 1;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
const list = Object.entries(freq)
|
|
998
|
-
.filter(([, n]) => n >= 2)
|
|
999
|
-
.sort((a, b) => b[1] - a[1])
|
|
1000
|
-
.slice(0, 150);
|
|
1001
|
-
|
|
1002
|
-
if (!list.length) {
|
|
1003
|
-
status.textContent = "Not enough words found.";
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
renderWordCloud(list);
|
|
1008
|
-
} catch (err) {
|
|
1009
|
-
status.textContent = "Error: " + err.message;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
function extractWordsFromMarkdown(text) {
|
|
1014
|
-
return text
|
|
1015
|
-
.replace(/```[\s\S]*?```/g, "")
|
|
1016
|
-
.replace(/`[^`\n]+`/g, "")
|
|
1017
|
-
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
1018
|
-
.replace(/https?:\/\/\S+/g, "")
|
|
1019
|
-
.replace(/[#*_~>`|!\[\](){}=\-+]/g, " ")
|
|
1020
|
-
.toLowerCase()
|
|
1021
|
-
.split(/[^a-zàâäéèêëïîôùûü']+/)
|
|
1022
|
-
.map((w) => w.replace(/^'+|'+$/g, ""))
|
|
1023
|
-
.filter((w) => w.length > 3 && !WC_STOP_WORDS.has(w));
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
function renderWordCloud(list) {
|
|
1027
|
-
const canvas = document.getElementById("wc-canvas");
|
|
1028
|
-
const body = document.getElementById("wc-body");
|
|
1029
|
-
canvas.width = body.clientWidth;
|
|
1030
|
-
canvas.height = body.clientHeight;
|
|
1031
|
-
|
|
1032
|
-
const isDark = document.documentElement.classList.contains("dark");
|
|
1033
|
-
const colors = isDark
|
|
1034
|
-
? [
|
|
1035
|
-
"#60a5fa",
|
|
1036
|
-
"#34d399",
|
|
1037
|
-
"#f9a8d4",
|
|
1038
|
-
"#a78bfa",
|
|
1039
|
-
"#fbbf24",
|
|
1040
|
-
"#6ee7b7",
|
|
1041
|
-
"#93c5fd",
|
|
1042
|
-
"#fb923c",
|
|
1043
|
-
]
|
|
1044
|
-
: [
|
|
1045
|
-
"#1d4ed8",
|
|
1046
|
-
"#047857",
|
|
1047
|
-
"#7c3aed",
|
|
1048
|
-
"#b45309",
|
|
1049
|
-
"#be123c",
|
|
1050
|
-
"#0369a1",
|
|
1051
|
-
"#4338ca",
|
|
1052
|
-
"#c2410c",
|
|
1053
|
-
];
|
|
1054
|
-
|
|
1055
|
-
const maxFreq = list[0][1];
|
|
1056
|
-
const wordList = list.map(([w, n]) => [
|
|
1057
|
-
w,
|
|
1058
|
-
Math.max(10, Math.round((72 * n) / maxFreq)),
|
|
1059
|
-
]);
|
|
1060
|
-
|
|
1061
|
-
document.getElementById("wc-status").classList.add("hidden");
|
|
1062
|
-
canvas.classList.remove("hidden");
|
|
1063
|
-
|
|
1064
|
-
WordCloud(canvas, {
|
|
1065
|
-
list: wordList,
|
|
1066
|
-
gridSize: Math.round((8 * canvas.width) / 1024),
|
|
1067
|
-
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
1068
|
-
color: () => colors[Math.floor(Math.random() * colors.length)],
|
|
1069
|
-
backgroundColor: isDark ? "#030712" : "#ffffff",
|
|
1070
|
-
rotateRatio: 0.3,
|
|
1071
|
-
minSize: 10,
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
function closeWordCloud() {
|
|
1076
|
-
document.getElementById("wc-overlay").classList.add("hidden");
|
|
1077
|
-
}
|
|
1078
914
|
|
|
1079
915
|
// Browser back/forward
|
|
1080
916
|
window.addEventListener("popstate", (e) => {
|