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
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
// ── Word Cloud — stop words & logic ───────────────────────────────────────────
|
|
2
|
+
// Loaded by index.html as a plain <script>; all symbols are global.
|
|
3
|
+
|
|
4
|
+
// ── Human stop words (English + French only) ──────────────────────────────────
|
|
5
|
+
const WC_STOP_WORDS = new Set([
|
|
6
|
+
// ── English ──
|
|
7
|
+
"the","and","for","are","but","not","you","all","this","that",
|
|
8
|
+
"with","have","from","they","will","one","been","can","has","was",
|
|
9
|
+
"more","also","when","there","their","what","about","which","would",
|
|
10
|
+
"into","than","then","each","just","over","after","such","here",
|
|
11
|
+
"its","your","our","some","were","very","only","out","had",
|
|
12
|
+
"she","his","her","him","who","how","any","other","these","those",
|
|
13
|
+
"being","may","use","used","using","should","could","shall","must",
|
|
14
|
+
"need","via","per","like","well","make","made","take","taken",
|
|
15
|
+
"same","both","between","before","while","where","since","still",
|
|
16
|
+
"even","able","back","come","down","does","done","good","much",
|
|
17
|
+
"said","them","want","way","without","within","whether","though",
|
|
18
|
+
"although","however","therefore","thus","hence","indeed","rather",
|
|
19
|
+
"either","neither","yet","once","upon","during","against","among",
|
|
20
|
+
"through","because","along","already","always","often","never",
|
|
21
|
+
"again","around","another","every","most","many","least","less",
|
|
22
|
+
"own","off","too","now","new","old","few","see","set",
|
|
23
|
+
"put","got","let","tell","know","think","seem","look","keep",
|
|
24
|
+
"give","show","hear","play","run","move","live","hold","turn",
|
|
25
|
+
"help","start","might","really","actually","simply","directly",
|
|
26
|
+
"basically","generally","normally","especially","currently",
|
|
27
|
+
// ── French ──
|
|
28
|
+
"les","des","une","pour","pas","sur","par","est","qui","que",
|
|
29
|
+
"dans","avec","sont","plus","tout","aux","mais","comme","vous",
|
|
30
|
+
"nous","leur","lui","elle","ils","elles","ces","ses","mon","ton",
|
|
31
|
+
"son","mes","tes","ainsi","donc","alors","car","peut","fait",
|
|
32
|
+
"encore","bien","aussi","très","même","entre","vers","dont","sans",
|
|
33
|
+
"sous","cette","celui","celle","ceux","celles","cela","ceci",
|
|
34
|
+
"avoir","être","faire","aller","voir","savoir","pouvoir","vouloir",
|
|
35
|
+
"devoir","partir","venir","prendre","mettre","dire","donner","tenir",
|
|
36
|
+
"tous","toutes","trop","peu","beaucoup","moins",
|
|
37
|
+
"jamais","toujours","souvent","parfois","déjà","bientôt",
|
|
38
|
+
"maintenant","après","avant","depuis","pendant","selon","afin",
|
|
39
|
+
"quand","parce","puisque","lorsque","tandis","quoique",
|
|
40
|
+
"cependant","néanmoins","pourtant","toutefois","ailleurs","ensuite",
|
|
41
|
+
"enfin","surtout","notamment","seulement","simplement","vraiment",
|
|
42
|
+
"lequel","laquelle","lesquels","lesquelles","duquel","auquel",
|
|
43
|
+
"desquels","auxquels","chaque","plusieurs","certains","certaines",
|
|
44
|
+
"quelques","autres","certaine","aucun","aucune","nul","nulle",
|
|
45
|
+
"chacun","chacune","quoi","quels","quelles","quel","quelle",
|
|
46
|
+
"votre","vos","notre","nos","leurs","puis","lors",
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// ── Language-specific stop words (keyed by file extension) ────────────────────
|
|
50
|
+
const WC_LANG_STOP_WORDS = {
|
|
51
|
+
// shared across all code files
|
|
52
|
+
_code: new Set([
|
|
53
|
+
"true","false","null","undefined","void","this","self","super",
|
|
54
|
+
"return","import","export","from","class","interface","extends","implements",
|
|
55
|
+
"public","private","protected","static","final","abstract","override",
|
|
56
|
+
"function","const","let","var","type","enum","struct","trait","impl",
|
|
57
|
+
"async","await","new","delete","typeof","instanceof","throw","throws",
|
|
58
|
+
"catch","finally","break","continue","default","switch","case","while",
|
|
59
|
+
"else","elif","pass","raise","yield","with","lambda","where","match",
|
|
60
|
+
"package","module","namespace","using","include","require","define",
|
|
61
|
+
"object","string","number","boolean","integer","float","double","long",
|
|
62
|
+
"byte","char","array","list","dict","tuple","bool","none",
|
|
63
|
+
"nil","print","console","stdout","stderr",
|
|
64
|
+
"size","length","count","index","value","result","error","data",
|
|
65
|
+
"args","argv","argc","opts","params","param","props","prop",
|
|
66
|
+
"node","root","tree","left","right","next","prev","head","tail",
|
|
67
|
+
"init","main","test","spec","mock","stub","util","helper","base",
|
|
68
|
+
"read","write","open","close","send","recv","load","save","parse",
|
|
69
|
+
"append","remove","insert","update","create","build",
|
|
70
|
+
"get","set","has","add","push","pull","move","copy","sort","find","filter","reduce","map",
|
|
71
|
+
]),
|
|
72
|
+
ts: new Set(["interface","namespace","readonly","keyof","infer","never","unknown","declare","satisfies","generic"]),
|
|
73
|
+
tsx: new Set(["interface","namespace","readonly","keyof","infer","never","unknown","declare","satisfies","generic","react","props","children","component","render","state","effect","hook","usestate","useeffect","useref","usecontext","usememo","usecallback"]),
|
|
74
|
+
js: new Set(["prototype","arguments","callee","getter","setter","proxy","promise","resolve","reject","then","catch"]),
|
|
75
|
+
jsx: new Set(["react","props","children","component","render","state","effect","hook"]),
|
|
76
|
+
java: new Set(["override","extends","implements","throws","final","synchronized","volatile","transient","strictfp","instanceof","assert","native","enum","annotation","autowired","bean","component","service","repository","controller","springframework","junit","lombok","getter","setter","builder","tostring","hashcode","equals","arraylist","hashmap","optional","stream","collectors","iterator","comparable","serializable","runnable","callable","exception","runtimeexception"]),
|
|
77
|
+
kt: new Set(["override","extends","object","companion","data","sealed","inner","inline","reified","crossinline","noinline","lateinit","lazy","apply","also","let","with","run","when","vararg","init","constructor","primary","secondary","coroutine","suspend","flow","stateflow","sharedflow","viewmodel","livedata","hilt","inject","module","provides","binds","qualifier","scope","composable","remember","mutablestate","launchedeffect","lifecycle"]),
|
|
78
|
+
py: new Set(["self","none","elif","lambda","yield","with","raise","assert","except","finally","pass","global","nonlocal","import","from","class","isinstance","issubclass","hasattr","getattr","setattr","delattr","super","property","staticmethod","classmethod","abstractmethod","dataclass","field","list","dict","tuple","bool","bytes","range","enumerate","print","input","open","close","append","extend","update","items","values","keys","format","strip","split","join","replace","lower","upper","startswith","endswith","type","len","int","str","float","repr","iter","next","zip","reversed","sorted","filter","reduce","partial","functools","itertools","collections","defaultdict","namedtuple","deque","heapq","bisect","contextlib","pathlib","datetime","argparse","logging","unittest","pytest","numpy","pandas","torch","tensorflow","flask","django","fastapi","sqlalchemy","pydantic","asyncio"]),
|
|
79
|
+
go: new Set(["func","chan","goroutine","defer","panic","recover","make","append","copy","close","delete","complex","imag","real","iota","blank","rune","byte","error","interface","struct","select","range","fallthrough","goto","nil","main","init","println","printf","sprintf","fprintf","scanf","sscanf","fscanf","strings","bytes","errors","context","sync","atomic","mutex","waitgroup","channel","handler","middleware","router","request","response","writer","reader","buffer","scanner","encoder","decoder","marshal","unmarshal"]),
|
|
80
|
+
rs: new Set(["let","mut","impl","trait","enum","struct","match","some","none","okay","unwrap","expect","clone","borrow","lifetime","ownership","move","copy","drop","result","option","vector","string","hashmap","hashset","btreemap","btreeset","refcell","mutex","rwlock","channel","sender","receiver","tokio","async","await","spawn","future","stream","iterator","closure","generic","where","derive","macro","println","format","panic","assert","eprintln","anyhow","thiserror","serde","warp","actix","axum","rocket","diesel","sqlx"]),
|
|
81
|
+
cs: new Set(["using","namespace","sealed","readonly","partial","virtual","abstract","override","base","object","string","bool","int","double","float","decimal","char","byte","short","long","uint","ulong","ushort","sbyte","nullable","async","await","task","void","delegate","event","linq","lambda","expression","predicate","action","func","tuple","list","dictionary","hashset","queue","stack","array","ienumerable","ienumerator","icollection","ilist","idictionary","console","debug","trace","exception","argumentexception","nullreferenceexception","invalidoperationexception","ioexception","attribute","annotation","property","getter","setter","constructor","disposable","dispose","garbage","collector","threading","semaphore","monitor","interlocked","concurrent","entity","framework","controller","service","repository","dependency","injection","middleware","startup","program","appsettings","configuration","logging","dbcontext","dbset","migrations"]),
|
|
82
|
+
swift: new Set(["guard","defer","where","some","opaque","associated","protocol","extension","mutating","nonmutating","lazy","weak","unowned","inout","subscript","operator","precedence","associativity","typealias","throw","rethrows","convenience","required","override","final","open","fileprivate","internal","public","private","static","class","struct","enum","actor","swiftui","view","body","state","binding","observedobject","stateobject","environmentobject","published","viewmodel","modifier","frame","padding","foregroundcolor","background","spacer","vstack","hstack","zstack","list","navigationview","navigationlink","sheet","alert","toolbar","button","text","image","textfield","toggle","picker","slider","stepper","scrollview","grid","path","shape","color","font","gesture","animation","transition","combine","publisher","subscriber","cancellable","sink","assign","flatmap","receive","debounce","throttle"]),
|
|
83
|
+
rb: new Set(["begin","rescue","ensure","raise","yield","lambda","proc","block","method","module","require","include","extend","prepend","attr","accessor","reader","writer","protected","private","public","freeze","frozen","dup","clone","object","class","instance","variable","symbol","hash","array","string","integer","float","boolean","nil","each","map","select","reject","inject","reduce","collect","detect","find","sort","uniq","flatten","compact","zip","take","drop","first","last","push","pull","shift","unshift","pop","min","max","sum","count","size","length","empty","any","all","none","include","respond","send","define","missing","inherited","extended","prepended","included","hook","callback","before","after","around","action","controller","model","view","route","migration","schema","database","activerecord","activemodel","actioncontroller","actionview","rails","rack","gem","bundle","rake","rspec","minitest","capybara","factory","stub","mock","double","expect","allow","receive"]),
|
|
84
|
+
html: new Set(["html","head","body","title","meta","link","script","style","div","span","section","article","header","footer","main","aside","nav","form","input","button","select","option","textarea","table","thead","tbody","tfoot","caption","colgroup","fieldset","legend","label","output","datalist","progress","details","summary","figure","figcaption","picture","source","video","audio","track","canvas","svg","path","circle","rect","line","polygon","polyline","defs","group","class","href","type","name","value","action","method","placeholder","required","checked","disabled","readonly","multiple","accept","enctype","autocomplete","autofocus","novalidate","pattern","minlength","maxlength","charset","content","lang","xmlns","viewport","initial","scale","description","keywords","author","refresh","equiv","property","role","aria","tabindex","draggable","contenteditable","spellcheck","translate","hidden","data","onclick","onchange","onsubmit","onload","onerror","onfocus","onblur","onkeydown","onkeyup","onmousedown","onmouseup","onmouseover","onmouseout","onmousemove","onscroll","onresize","tailwind"]),
|
|
85
|
+
css: new Set(["display","flex","grid","block","inline","position","relative","absolute","fixed","sticky","float","clear","margin","padding","border","outline","width","height","overflow","visibility","opacity","transform","transition","animation","color","background","font","size","weight","family","decoration","align","justify","content","items","self","order","grow","shrink","basis","wrap","direction","columns","rows","template","repeat","auto","minmax","span","area","place","object","fit","cursor","pointer","events","none","user","select","resize","zindex","layer","shadow","radius","filter","backdrop","blur","brightness","contrast","grayscale","hue","invert","saturate","sepia","rotate","scale","translate","skew","matrix","perspective","clip","mask","shape","outside","inside","overflow","ellipsis","nowrap","word","break","letter","spacing","indent","capitalize","uppercase","lowercase","white","space","vertical","baseline","middle","super","sub","list","counter","before","after","hover","focus","active","visited","disabled","checked","valid","invalid","required","optional","first","last","nth","child","root","empty","target","lang","media","screen","print","query","supports","charset","keyframes","viewport","placeholder","selection","scrollbar"]),
|
|
86
|
+
scss: new Set(["mixin","include","extend","each","while","else","error","warn","debug","forward","through","variable","interpolation","parent","selector","namespace","module","sass","scss","nesting","rule","declaration","property","value","default","global","important"]),
|
|
87
|
+
yml: new Set(["true","false","null","name","uses","with","runs","steps","jobs","needs","outputs","inputs","secrets","vars","environment","strategy","matrix","services","container","image","volumes","ports","networks","healthcheck","command","entrypoint","working","directory","timeout","retry","concurrency","permissions","defaults","reusable","workflow","trigger","push","pull","request","release","schedule","cron","branches","tags","paths","types","artifacts","cache","restore","keys","path","upload","download","deployment","approvals","reviewers","pages","packages","registry","docker","build","test","deploy","lint","coverage","report","notify","slack","email","include","exclude"]),
|
|
88
|
+
yaml: new Set(["true","false","null","name","uses","with","runs","steps","jobs","needs","outputs","inputs","secrets","vars","environment","strategy","matrix","services","container","image","volumes","ports","networks","healthcheck","command","entrypoint","working","directory","timeout","retry","concurrency","permissions","defaults","reusable","workflow","trigger","push","pull","request","release","schedule","cron","branches","tags","paths","types","artifacts","cache","restore","keys","path","upload","download","deployment","approvals","reviewers","pages","packages","registry","docker","build","test","deploy","lint","coverage","report","notify","slack","email","include","exclude"]),
|
|
89
|
+
json: new Set(["true","false","null","name","version","description","keywords","license","author","main","module","types","files","scripts","dependencies","devdependencies","peerdependencies","optionaldependencies","engines","repository","bugs","homepage","private","workspaces","exports","imports","type","config","publishconfig","resolutions","overrides"]),
|
|
90
|
+
xml: new Set(["version","encoding","xmlns","xsi","type","schema","location","element","attribute","complextype","simpletype","sequence","choice","restriction","extension","include","redefine","annotation","documentation","appinfo","notation","field","selector","unique","keyref","substitution","group","attributegroup","list","union","enumeration","pattern","minlength","maxlength","length","mininclusive","maxinclusive","minexclusive","maxexclusive","fractiondigits","totaldigits","whitespace","nillable","abstract","block","final","mixed","target","namespace","elementformdefault","attributeformdefault","blockdefault","finaldefault","lang","base","value","fixed","default","form","processcontents"]),
|
|
91
|
+
toml: new Set(["true","false","name","version","description","edition","authors","license","repository","homepage","documentation","keywords","categories","workspace","members","dependencies","devdependencies","builddependencies","features","profile","release","debug","test","bench","default","path","optional","package","build","resolver","patch","replace","source","registry","target","compiler","linker","rustflags","incremental","overflow","checks","panic","codegen","units","lto","strip","debuginfo","splitdebuginfo","rpath","binaries","examples","tests","benches","library"]),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
function wcBuildStopWords(exts) {
|
|
97
|
+
const combined = new Set(WC_STOP_WORDS);
|
|
98
|
+
const hasCode = exts.some((e) => e !== "md" && e !== "txt");
|
|
99
|
+
if (hasCode) {
|
|
100
|
+
for (const w of WC_LANG_STOP_WORDS._code) combined.add(w);
|
|
101
|
+
}
|
|
102
|
+
for (const ext of exts) {
|
|
103
|
+
const s = WC_LANG_STOP_WORDS[ext];
|
|
104
|
+
if (s) for (const w of s) combined.add(w);
|
|
105
|
+
}
|
|
106
|
+
return combined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function wcEsc(s) {
|
|
110
|
+
return String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function extractWordsFromMarkdown(text, stopWords = WC_STOP_WORDS) {
|
|
114
|
+
return text
|
|
115
|
+
// Strip import/package/require/include/using/namespace declarations
|
|
116
|
+
.replace(/^\s*(import|export\s+\{[^}]*\}|export\s+\*|package|require|#include|#import|using|namespace|from\s+['"][^'"]+['"]\s*(import)?|@[A-Za-z]+)\b.*/gm, "")
|
|
117
|
+
// Strip markdown code blocks
|
|
118
|
+
.replace(/```[\s\S]*?```/g, "")
|
|
119
|
+
.replace(/`[^`\n]+`/g, "")
|
|
120
|
+
// Strip markdown links (keep label)
|
|
121
|
+
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
|
|
122
|
+
// Strip URLs
|
|
123
|
+
.replace(/https?:\/\/\S+/g, "")
|
|
124
|
+
// Strip punctuation / operators
|
|
125
|
+
.replace(/[#*_~>`|!\[\](){}=\-+]/g, " ")
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
.split(/[^a-zàâäéèêëïîôùûü']+/)
|
|
128
|
+
.map((w) => w.replace(/^'+|'+$/g, ""))
|
|
129
|
+
.filter((w) => w.length > 3 && !stopWords.has(w));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function renderWordCloud(list) {
|
|
133
|
+
const canvas = document.getElementById("wc-canvas");
|
|
134
|
+
const body = document.getElementById("wc-body");
|
|
135
|
+
canvas.width = body.clientWidth;
|
|
136
|
+
canvas.height = body.clientHeight;
|
|
137
|
+
|
|
138
|
+
const isDark = document.documentElement.classList.contains("dark");
|
|
139
|
+
const colors = isDark
|
|
140
|
+
? ["#60a5fa","#34d399","#f9a8d4","#a78bfa","#fbbf24","#6ee7b7","#93c5fd","#fb923c"]
|
|
141
|
+
: ["#1d4ed8","#047857","#7c3aed","#b45309","#be123c","#0369a1","#4338ca","#c2410c"];
|
|
142
|
+
|
|
143
|
+
const maxFreq = list[0][1];
|
|
144
|
+
const wordList = list.map(([w, n]) => [w, Math.max(10, Math.round((72 * n) / maxFreq))]);
|
|
145
|
+
|
|
146
|
+
document.getElementById("wc-status").classList.add("hidden");
|
|
147
|
+
canvas.classList.remove("hidden");
|
|
148
|
+
|
|
149
|
+
WordCloud(canvas, {
|
|
150
|
+
list: wordList,
|
|
151
|
+
gridSize: Math.round((8 * canvas.width) / 1024),
|
|
152
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
153
|
+
color: () => colors[Math.floor(Math.random() * colors.length)],
|
|
154
|
+
backgroundColor: isDark ? "#030712" : "#ffffff",
|
|
155
|
+
rotateRatio: 0.3,
|
|
156
|
+
minSize: 10,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Browser (folder picker) ───────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
let _wcBrowseParent = null;
|
|
163
|
+
let _wcBrowseCurrent = "";
|
|
164
|
+
|
|
165
|
+
function wcToggleBrowser() {
|
|
166
|
+
const browser = document.getElementById("wc-browser");
|
|
167
|
+
const isHidden = browser.classList.toggle("hidden");
|
|
168
|
+
if (!isHidden) wcLoadBrowse(_wcBrowseCurrent || document.getElementById("wc-root").value || "/");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function wcLoadBrowse(dirPath) {
|
|
172
|
+
const list = document.getElementById("wc-browse-list");
|
|
173
|
+
list.innerHTML = '<p class="px-3 py-4 text-xs text-gray-400 text-center">Loading…</p>';
|
|
174
|
+
try {
|
|
175
|
+
const data = await fetch("/api/browse?path=" + encodeURIComponent(dirPath)).then((r) => r.json());
|
|
176
|
+
_wcBrowseCurrent = data.current;
|
|
177
|
+
_wcBrowseParent = data.parent;
|
|
178
|
+
document.getElementById("wc-browse-path").textContent = data.current;
|
|
179
|
+
document.getElementById("wc-browse-up").disabled = !data.parent;
|
|
180
|
+
|
|
181
|
+
const rows = data.dirs.map((dir) =>
|
|
182
|
+
`<button data-path="${wcEsc(dir.path)}" onclick="wcLoadBrowse(this.dataset.path)"
|
|
183
|
+
class="w-full flex items-center gap-2 px-3 py-2 text-sm text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
|
184
|
+
<span class="text-gray-400 shrink-0">📁</span>
|
|
185
|
+
<span class="text-gray-700 dark:text-gray-300 truncate">${wcEsc(dir.name)}</span>
|
|
186
|
+
</button>`
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
const selectBtn = `<button onclick="wcSelectFolder()" class="w-full px-3 py-2 text-xs text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-950/30 text-left font-medium border-t border-gray-100 dark:border-gray-800">
|
|
190
|
+
✓ Select: <span class="font-mono">${wcEsc(data.current)}</span>
|
|
191
|
+
</button>`;
|
|
192
|
+
|
|
193
|
+
list.innerHTML = (rows.length
|
|
194
|
+
? rows.join("")
|
|
195
|
+
: '<p class="px-3 py-3 text-xs text-gray-400 text-center">No sub-folders</p>'
|
|
196
|
+
) + selectBtn;
|
|
197
|
+
} catch {
|
|
198
|
+
list.innerHTML = '<p class="px-3 py-4 text-xs text-red-400 text-center">Cannot read directory</p>';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function wcBrowseUp() {
|
|
203
|
+
if (_wcBrowseParent) wcLoadBrowse(_wcBrowseParent);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function wcSelectFolder() {
|
|
207
|
+
document.getElementById("wc-root").value = _wcBrowseCurrent;
|
|
208
|
+
localStorage.setItem("wc-root", _wcBrowseCurrent);
|
|
209
|
+
document.getElementById("wc-browser").classList.add("hidden");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Persistence (localStorage) ────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
function wcSaveExts() {
|
|
215
|
+
const exts = [...document.querySelectorAll(".wc-ext:checked")].map((cb) => cb.value);
|
|
216
|
+
localStorage.setItem("wc-exts", JSON.stringify(exts));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function wcRestorePrefs() {
|
|
220
|
+
const savedRoot = localStorage.getItem("wc-root");
|
|
221
|
+
if (savedRoot) {
|
|
222
|
+
document.getElementById("wc-root").value = savedRoot;
|
|
223
|
+
_wcBrowseCurrent = savedRoot;
|
|
224
|
+
}
|
|
225
|
+
const savedExts = localStorage.getItem("wc-exts");
|
|
226
|
+
if (savedExts) {
|
|
227
|
+
try {
|
|
228
|
+
const exts = JSON.parse(savedExts);
|
|
229
|
+
document.querySelectorAll(".wc-ext").forEach((cb) => {
|
|
230
|
+
cb.checked = exts.includes(cb.value);
|
|
231
|
+
});
|
|
232
|
+
} catch { /* ignore corrupt data */ }
|
|
233
|
+
}
|
|
234
|
+
document.querySelectorAll(".wc-ext").forEach((cb) => {
|
|
235
|
+
cb.addEventListener("change", wcSaveExts);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ── Open / Launch / Close ─────────────────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
async function openWordCloud() {
|
|
242
|
+
const overlay = document.getElementById("wc-overlay");
|
|
243
|
+
const status = document.getElementById("wc-status");
|
|
244
|
+
const canvas = document.getElementById("wc-canvas");
|
|
245
|
+
overlay.classList.remove("hidden");
|
|
246
|
+
status.textContent = "Choose a root folder and click Launch.";
|
|
247
|
+
status.classList.remove("hidden");
|
|
248
|
+
canvas.classList.add("hidden");
|
|
249
|
+
|
|
250
|
+
const rootInput = document.getElementById("wc-root");
|
|
251
|
+
if (!rootInput.value) {
|
|
252
|
+
try {
|
|
253
|
+
const cfg = await fetch("/api/config").then((r) => r.json());
|
|
254
|
+
if (cfg.docsFolder) {
|
|
255
|
+
rootInput.value = cfg.docsFolder;
|
|
256
|
+
_wcBrowseCurrent = cfg.docsFolder;
|
|
257
|
+
}
|
|
258
|
+
} catch { /* ignore */ }
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function launchWordCloud() {
|
|
263
|
+
const status = document.getElementById("wc-status");
|
|
264
|
+
const canvas = document.getElementById("wc-canvas");
|
|
265
|
+
const root = document.getElementById("wc-root").value.trim();
|
|
266
|
+
|
|
267
|
+
if (!root) {
|
|
268
|
+
status.textContent = "Please select a root folder first.";
|
|
269
|
+
status.classList.remove("hidden");
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const exts = [...document.querySelectorAll(".wc-ext:checked")].map((cb) => cb.value);
|
|
274
|
+
if (!exts.length) {
|
|
275
|
+
status.textContent = "Please select at least one extension.";
|
|
276
|
+
status.classList.remove("hidden");
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
document.getElementById("wc-browser").classList.add("hidden");
|
|
281
|
+
status.textContent = "Reading files…";
|
|
282
|
+
status.classList.remove("hidden");
|
|
283
|
+
canvas.classList.add("hidden");
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const params = new URLSearchParams({ path: root });
|
|
287
|
+
exts.forEach((e) => params.append("ext", e));
|
|
288
|
+
const res = await fetch("/api/wordcloud?" + params);
|
|
289
|
+
if (!res.ok) {
|
|
290
|
+
const err = await res.json();
|
|
291
|
+
status.textContent = "Error: " + (err.error || res.statusText);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const { files, text } = await res.json();
|
|
295
|
+
status.textContent = `Analyzing ${files} file(s)…`;
|
|
296
|
+
|
|
297
|
+
const stopWords = wcBuildStopWords(exts);
|
|
298
|
+
const freq = {};
|
|
299
|
+
for (const w of extractWordsFromMarkdown(text, stopWords)) {
|
|
300
|
+
freq[w] = (freq[w] || 0) + 1;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const list = Object.entries(freq)
|
|
304
|
+
.filter(([, n]) => n >= 2)
|
|
305
|
+
.sort((a, b) => b[1] - a[1])
|
|
306
|
+
.slice(0, 150);
|
|
307
|
+
|
|
308
|
+
if (!list.length) {
|
|
309
|
+
status.textContent = "Not enough words found.";
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
renderWordCloud(list);
|
|
314
|
+
} catch (err) {
|
|
315
|
+
status.textContent = "Error: " + err.message;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function closeWordCloud() {
|
|
320
|
+
document.getElementById("wc-overlay").classList.add("hidden");
|
|
321
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wordcloud.d.ts","sourceRoot":"","sources":["../../../src/routes/wordcloud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAsBpD,wBAAgB,eAAe,IAAI,MAAM,CAyCxC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.wordcloudRouter = wordcloudRouter;
|
|
7
|
+
const express_1 = require("express");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
function collectFiles(dir, exts, out = []) {
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
for (const e of entries) {
|
|
19
|
+
if (e.isDirectory() && !e.name.startsWith('.')) {
|
|
20
|
+
collectFiles(path_1.default.join(dir, e.name), exts, out);
|
|
21
|
+
}
|
|
22
|
+
else if (e.isFile()) {
|
|
23
|
+
const ext = path_1.default.extname(e.name).slice(1).toLowerCase();
|
|
24
|
+
if (exts.includes(ext))
|
|
25
|
+
out.push(path_1.default.join(dir, e.name));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function wordcloudRouter() {
|
|
31
|
+
const router = (0, express_1.Router)();
|
|
32
|
+
// GET /api/wordcloud?path=<absolute_path>&ext=md&ext=ts&ext=java
|
|
33
|
+
// Returns { files: number, text: string }
|
|
34
|
+
router.get('/', (req, res) => {
|
|
35
|
+
const rawPath = req.query.path || '/';
|
|
36
|
+
const target = path_1.default.resolve(rawPath);
|
|
37
|
+
const rawExt = req.query.ext;
|
|
38
|
+
const exts = rawExt
|
|
39
|
+
? (Array.isArray(rawExt) ? rawExt : [rawExt]).map((e) => e.toLowerCase().replace(/^\./, ''))
|
|
40
|
+
: ['md'];
|
|
41
|
+
let stat;
|
|
42
|
+
try {
|
|
43
|
+
stat = fs_1.default.statSync(target);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
res.status(400).json({ error: 'Path not found' });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let files;
|
|
50
|
+
if (stat.isFile()) {
|
|
51
|
+
const ext = path_1.default.extname(target).slice(1).toLowerCase();
|
|
52
|
+
files = exts.includes(ext) ? [target] : [];
|
|
53
|
+
}
|
|
54
|
+
else if (stat.isDirectory()) {
|
|
55
|
+
files = collectFiles(target, exts);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
res.status(400).json({ error: 'Path must be a directory or a file' });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const text = files
|
|
62
|
+
.map((f) => { try {
|
|
63
|
+
return fs_1.default.readFileSync(f, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return '';
|
|
67
|
+
} })
|
|
68
|
+
.join('\n\n');
|
|
69
|
+
res.json({ files: files.length, text });
|
|
70
|
+
});
|
|
71
|
+
return router;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=wordcloud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wordcloud.js","sourceRoot":"","sources":["../../../src/routes/wordcloud.ts"],"names":[],"mappings":"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"}
|
package/dist/src/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,EAChC,QAAQ,EACR,IAAI,EACJ,WAAmB,GACpB,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD/B"}
|
package/dist/src/server.js
CHANGED
|
@@ -12,6 +12,7 @@ const config_1 = require("./routes/config");
|
|
|
12
12
|
const browse_1 = require("./routes/browse");
|
|
13
13
|
const images_1 = require("./routes/images");
|
|
14
14
|
const diagrams_1 = require("./routes/diagrams");
|
|
15
|
+
const wordcloud_1 = require("./routes/wordcloud");
|
|
15
16
|
const config_2 = require("./lib/config");
|
|
16
17
|
async function startServer({ docsPath, port, openBrowser = false, }) {
|
|
17
18
|
const app = (0, express_1.default)();
|
|
@@ -24,6 +25,7 @@ async function startServer({ docsPath, port, openBrowser = false, }) {
|
|
|
24
25
|
app.use('/api/browse', (0, browse_1.browseRouter)());
|
|
25
26
|
app.use('/api/images', (0, images_1.imagesRouter)(docsPath));
|
|
26
27
|
app.use('/api/diagrams', (0, diagrams_1.diagramsRouter)(docsPath));
|
|
28
|
+
app.use('/api/wordcloud', (0, wordcloud_1.wordcloudRouter)());
|
|
27
29
|
// Static frontend assets
|
|
28
30
|
const frontendPath = path_1.default.join(__dirname, 'frontend');
|
|
29
31
|
app.use(express_1.default.static(frontendPath));
|
package/dist/src/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
|