jsdoc-scribe 1.0.1 → 1.7.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.
@@ -0,0 +1,575 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Themes
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const THEMES = {
10
+ default: {
11
+ light: `:root{--bg:#f8f9fc;--surface:#fff;--border:#e0e4f0;--text:#1a1a2e;--text2:#666;--text3:#888;--sidebar-bg:#1a1a2e;--sidebar-text:#c8d3f0;--sidebar-active:#2d2d4e;--sidebar-title:#7986cb;--accent:#4361ee;--accent2:#e8eaf6;--search-bg:#2d2d4e;--search-border:#3a3a5e;--search-text:#e0e4f8;--search-panel:#252543;--th-bg:#f5f6fa;--code-bg:#f5f6fa;--sub-label:#7986cb;--method-border:#f0f0f0;--dep-bg:#fff8e1;--dep-text:#e65100;--throws-bg:#fce4ec;--throws-text:#c62828}`,
12
+ dark: `[data-theme=dark]{--bg:#0f0f1a;--surface:#1a1a2e;--border:#2d2d4e;--text:#e0e4f8;--text2:#9aa5c8;--text3:#5a6494;--sidebar-bg:#0a0a14;--sidebar-text:#9aa5c8;--sidebar-active:#1a1a2e;--sidebar-title:#5a6494;--accent:#6b8cff;--accent2:#1a1f3a;--search-bg:#1a1a2e;--search-border:#2d2d4e;--search-text:#c8d3f0;--search-panel:#0f0f1a;--th-bg:#1a1a2e;--code-bg:#1a1a2e;--sub-label:#5a6494;--method-border:#2d2d4e;--dep-bg:#2d2000;--dep-text:#ffb300;--throws-bg:#2d0a14;--throws-text:#ef9a9a}`,
13
+ toggleBtn: true,
14
+ },
15
+ minimal: {
16
+ light: `:root{--bg:#fff;--surface:#fafafa;--border:#e8e8e8;--text:#222;--text2:#555;--text3:#888;--sidebar-bg:#f5f5f5;--sidebar-text:#444;--sidebar-active:#e8e8e8;--sidebar-title:#888;--accent:#0066cc;--accent2:#e8f0fe;--search-bg:#fff;--search-border:#ddd;--search-text:#222;--search-panel:#fff;--th-bg:#f5f5f5;--code-bg:#f5f5f5;--sub-label:#888;--method-border:#efefef;--dep-bg:#fff8e1;--dep-text:#b36b00;--throws-bg:#fff0f0;--throws-text:#c00}`,
17
+ dark: ``,
18
+ toggleBtn: false,
19
+ },
20
+ dark: {
21
+ light: `:root{--bg:#0f0f1a;--surface:#1a1a2e;--border:#2d2d4e;--text:#e0e4f8;--text2:#9aa5c8;--text3:#5a6494;--sidebar-bg:#0a0a14;--sidebar-text:#9aa5c8;--sidebar-active:#1a1a2e;--sidebar-title:#5a6494;--accent:#6b8cff;--accent2:#1a1f3a;--search-bg:#1a1a2e;--search-border:#2d2d4e;--search-text:#c8d3f0;--search-panel:#0f0f1a;--th-bg:#1a1a2e;--code-bg:#1a1a2e;--sub-label:#5a6494;--method-border:#2d2d4e;--dep-bg:#2d2000;--dep-text:#ffb300;--throws-bg:#2d0a14;--throws-text:#ef9a9a}`,
22
+ dark: ``,
23
+ toggleBtn: false,
24
+ },
25
+ };
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // CSS (shared structural rules; colors come from theme vars above)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const CSS_STRUCTURE = `
32
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
33
+ body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:15px;line-height:1.6;color:var(--text);background:var(--bg);transition:background .2s,color .2s}
34
+ a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}
35
+ code,pre{font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace}
36
+ .layout{display:flex;min-height:100vh}
37
+ .sidebar{width:272px;min-width:272px;background:var(--sidebar-bg);color:var(--sidebar-text);padding:0;display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow-y:auto}
38
+ .sidebar-header{padding:18px 20px 12px;display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
39
+ .sidebar-header a{color:var(--text);font-size:15px;font-weight:700}
40
+ .sidebar-header .version{display:block;font-size:11px;color:var(--sidebar-title);margin-top:2px}
41
+ .theme-btn{background:none;border:1px solid var(--search-border);border-radius:5px;padding:3px 8px;font-size:12px;color:var(--sidebar-text);cursor:pointer;white-space:nowrap;flex-shrink:0;margin-top:2px}
42
+ .theme-btn:hover{background:var(--sidebar-active)}
43
+ .search-wrap{position:relative;padding:0 14px 12px;border-bottom:1px solid var(--search-border)}
44
+ .search-box{width:100%;background:var(--search-bg);border:1px solid var(--search-border);border-radius:6px;padding:7px 10px 7px 32px;color:var(--search-text);font-size:13px;outline:none}
45
+ .search-box::placeholder{color:var(--sidebar-title)}
46
+ .search-box:focus{border-color:var(--accent)}
47
+ .search-results{display:none;position:absolute;left:14px;right:14px;background:var(--search-panel);border:1px solid var(--search-border);border-radius:6px;max-height:320px;overflow-y:auto;z-index:100;margin-top:2px}
48
+ .search-results.visible{display:block}
49
+ .search-result-item{display:block;padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--search-border);text-decoration:none}
50
+ .search-result-item:hover{background:var(--sidebar-active)}
51
+ .sr-name{font-size:13px;font-weight:600;color:var(--search-text);font-family:monospace}
52
+ .sr-kind{font-size:11px;color:var(--sidebar-title);margin-left:6px}
53
+ .sr-module{font-size:11px;color:var(--sidebar-title);display:block;margin-top:1px;opacity:.7}
54
+ .search-no-results{padding:10px 12px;color:var(--sidebar-title);font-size:13px}
55
+ .sidebar-section{padding:10px 0 4px}
56
+ .sidebar-section-title{padding:4px 20px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--sidebar-title)}
57
+ .sidebar-link{display:block;padding:5px 20px;font-size:13px;color:var(--sidebar-text);transition:background .1s}
58
+ .sidebar-link:hover,.sidebar-link.active{background:var(--sidebar-active);color:var(--text);text-decoration:none}
59
+ .sidebar-dir-toggle{display:flex;align-items:center;gap:4px;cursor:pointer;list-style:none;padding:5px 20px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--sidebar-title);user-select:none}
60
+ .sidebar-dir-toggle::-webkit-details-marker{display:none}
61
+ .sidebar-dir-toggle::before{content:'▶';font-size:8px;transition:transform .15s}
62
+ details[open] .sidebar-dir-toggle::before{transform:rotate(90deg)}
63
+ .sidebar-link-indent{padding-left:32px}
64
+ .main{flex:1;padding:40px 48px;max-width:960px}
65
+ .page-title{font-size:28px;font-weight:700;color:var(--text);margin-bottom:4px}
66
+ .page-subtitle{color:var(--text2);font-size:13px;margin-bottom:12px}
67
+ .module-desc{color:var(--text2);font-size:14px;line-height:1.6;margin-bottom:28px;max-width:700px}
68
+ .section{margin-bottom:40px}
69
+ .section-title{font-size:18px;font-weight:700;color:var(--text);margin-bottom:14px;padding-bottom:8px;border-bottom:2px solid var(--accent2)}
70
+ .card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:18px 22px;margin-bottom:10px;scroll-margin-top:24px}
71
+ .card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
72
+ .card-name{font-size:15px;font-weight:700;color:var(--text);font-family:monospace}
73
+ .card-sig{font-size:12px;color:var(--text2);margin-top:3px;font-family:monospace;word-break:break-all}
74
+ .card-desc{font-size:13px;color:var(--text2);margin-top:8px;line-height:1.5}
75
+ .card-example{margin-top:10px}
76
+ .card-example pre{background:var(--code-bg);border:1px solid var(--border);border-radius:6px;padding:10px 14px;font-size:12px;overflow-x:auto;color:var(--text)}
77
+ .copy-btn{flex-shrink:0;background:none;border:1px solid var(--border);border-radius:5px;padding:3px 8px;font-size:11px;color:var(--text3);cursor:pointer;transition:all .15s;white-space:nowrap}
78
+ .copy-btn:hover{background:var(--accent2);border-color:var(--accent);color:var(--accent)}
79
+ .copy-btn.copied{background:#e8f5e9;border-color:#43a047;color:#2e7d32}
80
+ .badge{display:inline-block;padding:2px 7px;border-radius:4px;font-size:11px;font-weight:600;margin-right:3px;margin-top:5px}
81
+ .badge-exported{background:#e8f5e9;color:#2e7d32}
82
+ .badge-async{background:#e3f2fd;color:#1565c0}
83
+ .badge-abstract{background:#fce4ec;color:#c62828}
84
+ .badge-static{background:#fff3e0;color:#e65100}
85
+ .badge-readonly{background:#f3e5f5;color:#6a1b9a}
86
+ .badge-generator{background:#e0f2f1;color:#00695c}
87
+ .badge-private{background:#fafafa;color:#666;border:1px solid #ddd}
88
+ .badge-protected{background:#fff8e1;color:#f57f17}
89
+ .badge-public{background:#f5f5f5;color:#555}
90
+ .badge-optional{background:#f3e5f5;color:#6a1b9a}
91
+ .badge-const{background:#ede7f6;color:#4527a0}
92
+ .badge-var{background:#fff3e0;color:#e65100}
93
+ .badge-deprecated{background:var(--dep-bg);color:var(--dep-text)}
94
+ .badge-since{background:#e8f5e9;color:#2e7d32}
95
+ .deprecated-notice{background:var(--dep-bg);color:var(--dep-text);border-radius:5px;padding:6px 12px;font-size:12px;margin-top:8px}
96
+ .since-label{font-size:11px;color:var(--text3);margin-top:4px}
97
+ .throws-table{width:100%;border-collapse:collapse;margin-top:8px;font-size:13px;border:1px solid var(--throws-bg)}
98
+ .throws-table th{text-align:left;padding:5px 10px;background:var(--throws-bg);color:var(--throws-text);font-weight:600;font-size:12px}
99
+ .throws-table td{padding:5px 10px;border-top:1px solid var(--border);color:var(--text);vertical-align:top}
100
+ .throws-table td code{font-size:12px}
101
+ .params-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:13px}
102
+ .params-table th{text-align:left;padding:6px 10px;background:var(--th-bg);color:var(--text2);font-weight:600;border-bottom:2px solid var(--border)}
103
+ .params-table td{padding:6px 10px;border-bottom:1px solid var(--method-border);vertical-align:top;color:var(--text)}
104
+ .params-table td code{background:var(--code-bg);padding:1px 5px;border-radius:3px;font-size:12px}
105
+ .returns{margin-top:8px;font-size:13px;color:var(--text2)}
106
+ .returns code{background:var(--code-bg);padding:1px 6px;border-radius:3px;font-family:monospace}
107
+ .collapse-toggle{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;list-style:none;margin-top:14px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--sub-label);padding:0}
108
+ .collapse-toggle::-webkit-details-marker{display:none}
109
+ .collapse-toggle::before{content:'▶';font-size:9px;display:inline-block;transition:transform .15s;color:var(--sub-label)}
110
+ details[open] .collapse-toggle::before{transform:rotate(90deg)}
111
+ .collapse-body{margin-top:4px}
112
+ .method-row{margin-top:8px;padding:10px 0;border-top:1px solid var(--method-border)}
113
+ .method-sig{font-family:monospace;font-size:13px;color:var(--text)}
114
+ .method-desc{font-size:12px;color:var(--text2);margin-top:4px}
115
+ .module-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px;margin-top:4px}
116
+ .module-card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:16px 20px;transition:border-color .15s,box-shadow .15s}
117
+ .module-card:hover{border-color:var(--accent);box-shadow:0 2px 8px rgba(67,97,238,.1);text-decoration:none}
118
+ .module-card-name{font-size:14px;font-weight:700;color:var(--text);font-family:monospace}
119
+ .module-card-stats{font-size:12px;color:var(--text3);margin-top:4px}
120
+ .module-card-desc{font-size:12px;color:var(--text3);margin-top:6px;line-height:1.4}
121
+ .breadcrumb{font-size:13px;color:var(--text3);margin-bottom:20px}
122
+ .breadcrumb a{color:var(--accent)}
123
+ .anchor-link{color:var(--sidebar-title);opacity:0;font-size:13px;margin-left:6px;transition:opacity .15s}
124
+ .card:hover .anchor-link{opacity:.6}
125
+ .anchor-link:hover{opacity:1;text-decoration:none}
126
+ .empty{color:var(--text3);font-size:13px;font-style:italic}
127
+ .link-ref{color:var(--accent);text-decoration:none;font-size:inherit}
128
+ .link-ref:hover{text-decoration:underline}
129
+ .sr-body{font-size:11px;color:var(--sidebar-title);display:block;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}
130
+ .sr-preview{font-size:11px;color:var(--text3);font-style:italic;display:block;margin-top:2px}
131
+
132
+ .source-link{font-size:11px;color:var(--text3);font-family:monospace;text-decoration:none;margin-left:6px;opacity:.7}
133
+ .source-link:hover{opacity:1;text-decoration:underline;color:var(--accent)}
134
+
135
+ `;
136
+
137
+ const CLIENT_JS = `
138
+ (function(){
139
+ var THEME_KEY='jsdoc-scribe-theme';
140
+ var saved=localStorage.getItem(THEME_KEY);
141
+ if(saved) document.documentElement.setAttribute('data-theme',saved);
142
+ var btn=document.getElementById('theme-btn');
143
+ if(btn){
144
+ btn.textContent=(saved==='dark')?'Light':'Dark';
145
+ btn.addEventListener('click',function(){
146
+ var cur=document.documentElement.getAttribute('data-theme');
147
+ var next=cur==='dark'?'light':'dark';
148
+ document.documentElement.setAttribute('data-theme',next);
149
+ localStorage.setItem(THEME_KEY,next);
150
+ btn.textContent=next==='dark'?'Light':'Dark';
151
+ });
152
+ }
153
+ document.addEventListener('click',function(e){
154
+ var b=e.target.closest('.copy-btn');
155
+ if(!b) return;
156
+ navigator.clipboard.writeText(b.dataset.sig||'').then(function(){
157
+ b.textContent='Copied!';b.classList.add('copied');
158
+ setTimeout(function(){b.textContent='Copy';b.classList.remove('copied');},1500);
159
+ });
160
+ });
161
+ var INDEX=window.__SEARCH_INDEX__||[];
162
+ var box=document.getElementById('search-box');
163
+ var panel=document.getElementById('search-results');
164
+ if(!box||!panel) return;
165
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
166
+ function render(items){
167
+ panel.innerHTML=items.length
168
+ ?items.slice(0,20).map(function(r){return'<a class="search-result-item" href="'+r.url+'"><span class="sr-name">'+esc(r.name)+'</span><span class="sr-kind">'+esc(r.kind)+'</span><span class="sr-module">'+esc(r.module)+'</span>+(r.body?'<span class="sr-preview">'+esc(r.body.slice(0,80))+'</span>':'')+'</a>';}).join('')
169
+ :'<div class="search-no-results">No results</div>';
170
+ panel.classList.add('visible');
171
+ }
172
+ function search(q){
173
+ q=q.trim().toLowerCase();
174
+ if(!q){panel.classList.remove('visible');return;}
175
+ render(INDEX.filter(function(r){
176
+ return r.name.toLowerCase().includes(q)
177
+ ||r.module.toLowerCase().includes(q)
178
+ ||(r.body&&r.body.toLowerCase().includes(q));
179
+ }));
180
+ }
181
+ box.addEventListener('input',function(){search(box.value);});
182
+ box.addEventListener('focus',function(){if(box.value.trim())search(box.value);});
183
+ document.addEventListener('click',function(e){if(!box.contains(e.target)&&!panel.contains(e.target))panel.classList.remove('visible');});
184
+ document.addEventListener('keydown',function(e){
185
+ if((e.metaKey||e.ctrlKey)&&e.key==='k'){e.preventDefault();box.focus();box.select();}
186
+ if(e.key==='Escape')panel.classList.remove('visible');
187
+ });
188
+ })();
189
+ `;
190
+
191
+ // ---------------------------------------------------------------------------
192
+ // Helpers
193
+ // ---------------------------------------------------------------------------
194
+
195
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
196
+ function badge(label,cls){return'<span class="badge badge-'+cls+'">'+esc(label)+'</span>';}
197
+ function anchorId(kind,name){return kind+'-'+name.replace(/[^a-zA-Z0-9_]/g,'_');}
198
+ function copyBtn(sig){return'<button class="copy-btn" data-sig="'+esc(sig)+'" title="Copy">Copy</button>';}
199
+
200
+ function metaHtml(item){
201
+ var out='';
202
+ if(item.deprecated!=null) out+='<div class="deprecated-notice">&#9888; Deprecated'+(item.deprecated?': '+esc(item.deprecated):'')+'</div>';
203
+ if(item.since) out+='<div class="since-label">Since v'+esc(item.since)+'</div>';
204
+ return out;
205
+ }
206
+
207
+ function descHtml(item,symbolMap,filePath){
208
+ var out='';
209
+ if(item.description) out+='<div class="card-desc">'+(symbolMap?resolveLinks(item.description,symbolMap,filePath):esc(item.description))+'</div>';
210
+ out+=metaHtml(item);
211
+ if(item.example) out+='<div class="card-example"><pre>'+esc(item.example)+'</pre></div>';
212
+ return out;
213
+ }
214
+
215
+ function renderParams(params, jsdocParams){
216
+ if(!params||!params.length) return '';
217
+ // Build a lookup of JSDoc @param enrichments keyed by name
218
+ var jmap={};
219
+ (jsdocParams||[]).forEach(function(p){jmap[p.name]=p;});
220
+ var html='<table class="params-table"><thead><tr><th>Parameter</th><th>Type</th><th>Optional</th><th>Description</th></tr></thead><tbody>';
221
+ params.forEach(function(p){
222
+ var jp=jmap[p.name]||{};
223
+ var type=jp.type&&jp.type!=='any'?jp.type:p.type;
224
+ html+='<tr><td><code>'+esc(p.name)+'</code></td><td><code>'+esc(type)+'</code></td><td>'+(p.optional||jp.optional?'yes':'')+'</td><td>'+(jp.description?esc(jp.description):'')+'</td></tr>';
225
+ });
226
+ return html+'</tbody></table>';
227
+ }
228
+
229
+ function renderReturns(returnType, returnsTag){
230
+ var type=returnsTag&&returnsTag.type&&returnsTag.type!=='any'?returnsTag.type:returnType;
231
+ var desc=returnsTag&&returnsTag.description?'— '+esc(returnsTag.description):'';
232
+ return'<div class="returns">Returns: <code>'+esc(type)+'</code> '+desc+'</div>';
233
+ }
234
+
235
+ function renderThrows(throws){
236
+ if(!throws||!throws.length) return '';
237
+ var html='<table class="throws-table"><thead><tr><th>Throws</th><th>Description</th></tr></thead><tbody>';
238
+ throws.forEach(function(t){html+='<tr><td><code>'+esc(t.type||'Error')+'</code></td><td>'+esc(t.description||'')+'</td></tr>';});
239
+ return html+'</tbody></table>';
240
+ }
241
+
242
+ function resolveLinks(text, symbolMap, filePath, moduleHtmlPathFn, modules){
243
+ if(!text||!symbolMap) return esc(text);
244
+ // Replace {@link Symbol} and {@link Symbol#method} with anchor tags
245
+ return esc(text).replace(/\{@link ([^}]+)\}/g, function(_, ref){
246
+ var parts=ref.trim().split('#');
247
+ var sym=parts[0].trim();
248
+ var method=parts[1]?parts[1].trim():null;
249
+ var entry=symbolMap[sym];
250
+ if(!entry) return'<code>'+esc(ref)+'</code>';
251
+ // Are we on the same module page?
252
+ var targetPath=entry.modulePath;
253
+ var href=targetPath===filePath?'':(targetPath||'');
254
+ if(method) href+='#meth-'+sym+'_'+method;
255
+ else href+=('#'+entry.anchorId);
256
+ return'<a href="'+href+'" class="link-ref"><code>'+esc(sym+(method?'.'+method:''))+'</code></a>';
257
+ });
258
+ }
259
+
260
+ function sourceLink(item, filePath, sourceUrl){
261
+ if(item.line==null) return '';
262
+ var label='line '+item.line;
263
+ if(sourceUrl){
264
+ var base=sourceUrl.replace(/\/$/,'');
265
+ var rel=filePath.replace(/\\/g,'/');
266
+ var href=base+'/'+rel+'#L'+item.line;
267
+ return'<a class="source-link" href="'+esc(href)+'" target="_blank" rel="noopener">'+esc(label)+'</a>';
268
+ }
269
+ return'<span class="source-link">'+esc(filePath.replace(/\\/g,'/'))+':'+item.line+'</span>';
270
+ }
271
+
272
+ function collapsible(label,html,open){
273
+ return'<details'+(open?' open':'')+'><summary class="collapse-toggle">'+esc(label)+'</summary><div class="collapse-body">'+html+'</div></details>';
274
+ }
275
+
276
+ function buildCss(theme){
277
+ var t=THEMES[theme]||THEMES.default;
278
+ return t.light+(t.dark||'')+CSS_STRUCTURE;
279
+ }
280
+
281
+ function page(title,sidebarHtml,bodyHtml,searchIndex,theme){
282
+ var t=THEMES[theme]||THEMES.default;
283
+ var themeJs=t.toggleBtn?CLIENT_JS:'(function(){var INDEX=window.__SEARCH_INDEX__||[];var box=document.getElementById("search-box");var panel=document.getElementById("search-results");if(!box||!panel)return;function esc(s){return String(s).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");}function render(items){panel.innerHTML=items.length?items.slice(0,20).map(function(r){return\'<a class="search-result-item" href="\'+r.url+\'"><span class="sr-name">\'+esc(r.name)+\'</span><span class="sr-kind">\'+esc(r.kind)+\'</span><span class="sr-module">\'+esc(r.module)+\'</span></a>\';}).join(""):\'<div class="search-no-results">No results</div>\';panel.classList.add("visible");}function search(q){q=q.trim().toLowerCase();if(!q){panel.classList.remove("visible");return;}render(INDEX.filter(function(r){return r.name.toLowerCase().includes(q)||r.module.toLowerCase().includes(q);}));}box.addEventListener("input",function(){search(box.value);});box.addEventListener("focus",function(){if(box.value.trim())search(box.value);});document.addEventListener("click",function(e){if(!box.contains(e.target)&&!panel.contains(e.target))panel.classList.remove("visible");});document.addEventListener("keydown",function(e){if((e.metaKey||e.ctrlKey)&&e.key==="k"){e.preventDefault();box.focus();box.select();}if(e.key==="Escape")panel.classList.remove("visible");});document.addEventListener("click",function(e){var b=e.target.closest(".copy-btn");if(!b)return;navigator.clipboard.writeText(b.dataset.sig||"").then(function(){b.textContent="Copied!";b.classList.add("copied");setTimeout(function(){b.textContent="Copy";b.classList.remove("copied");},1500);});});})();';
284
+ return'<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<meta name="viewport" content="width=device-width,initial-scale=1">\n<title>'+esc(title)+'</title>\n<style>'+buildCss(theme)+'</style>\n</head>\n<body>\n<div class="layout">\n<nav class="sidebar">'+sidebarHtml+'</nav>\n<main class="main">'+bodyHtml+'</main>\n</div>\n<script>window.__SEARCH_INDEX__='+JSON.stringify(searchIndex)+';</script>\n<script>'+themeJs+'</script>\n</body>\n</html>';
285
+ }
286
+
287
+ // ---------------------------------------------------------------------------
288
+ // Module label helpers
289
+ // ---------------------------------------------------------------------------
290
+
291
+ function commonRoot(modules){
292
+ if(!modules.length) return '';
293
+ var parts=modules.map(function(m){return m.filePath.replace(/\\/g,'/').split('/');});
294
+ var min=Math.min.apply(null,parts.map(function(p){return p.length;}))-1;
295
+ var i=0;
296
+ while(i<min&&parts.every(function(p){return p[i]===parts[0][i];})) i++;
297
+ return parts[0].slice(0,i).join('/');
298
+ }
299
+
300
+ function moduleLabel(filePath,modules){
301
+ var root=commonRoot(modules);
302
+ var rel=filePath.replace(/\\/g,'/');
303
+ if(root) rel=rel.slice(root.length).replace(/^\//,'');
304
+ return rel.replace(/\.[jt]sx?$/,'');
305
+ }
306
+
307
+ function moduleHtmlPath(filePath,modules){return'modules/'+moduleLabel(filePath,modules).replace(/\//g,'__')+'.html';}
308
+
309
+ // ---------------------------------------------------------------------------
310
+ // Search index
311
+ // ---------------------------------------------------------------------------
312
+
313
+ function buildBody(item){
314
+ var parts=[];
315
+ if(item.description) parts.push(item.description);
316
+ (item.jsdocParams||[]).forEach(function(p){if(p.description)parts.push(p.name+': '+p.description);});
317
+ if(item.returns&&item.returns.description) parts.push('returns: '+item.returns.description);
318
+ (item.throws||[]).forEach(function(t){if(t.description)parts.push('throws: '+t.description);});
319
+ return parts.join(' | ').slice(0,300)||null;
320
+ }
321
+
322
+ function buildSearchIndex(modules,prefix){
323
+ prefix=prefix||'';
324
+ var index=[];
325
+ modules.forEach(function(mod){
326
+ var rel=moduleHtmlPath(mod.filePath,modules);
327
+ var label=moduleLabel(mod.filePath,modules);
328
+ function url(a){return prefix+rel+'#'+a;}
329
+ function push(name,kind,anchor,item){index.push({name:name,kind:kind,module:label,url:url(anchor),body:buildBody(item||{})});}
330
+ mod.functions.forEach(function(f){push(f.name,'function',anchorId('fn',f.name),f);});
331
+ mod.classes.forEach(function(c){
332
+ push(c.name,'class',anchorId('cls',c.name),c);
333
+ c.methods.forEach(function(m){push(c.name+'.'+m.name,'method',anchorId('cls',c.name),m);});
334
+ });
335
+ mod.interfaces.forEach(function(i){push(i.name,'interface',anchorId('iface',i.name),i);});
336
+ mod.typeAliases.forEach(function(t){push(t.name,'type',anchorId('type',t.name),t);});
337
+ mod.enums.forEach(function(e){push(e.name,'enum',anchorId('enum',e.name),e);});
338
+ mod.variables.forEach(function(v){push(v.name,v.isConst?'const':'var',anchorId('var',v.name),v);});
339
+ });
340
+ return index;
341
+ }
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // Sidebar
345
+ // ---------------------------------------------------------------------------
346
+
347
+ function buildSidebar(modules,projectName,version,activePath,rootPrefix,showToggle){
348
+ var prefix=rootPrefix||'';
349
+ var html='<div class="sidebar-header">'
350
+ +'<div><a href="'+prefix+'index.html">'+esc(projectName)+'</a>'+(version?'<span class="version">v'+esc(version)+'</span>':'')+'</div>'
351
+ +(showToggle!==false?'<button id="theme-btn" class="theme-btn">Dark</button>':'')
352
+ +'</div>'
353
+ +'<div class="search-wrap"><input id="search-box" class="search-box" type="search" placeholder="Search... (Ctrl+K)" autocomplete="off"><div id="search-results" class="search-results"></div></div>'
354
+ +'<div class="sidebar-section"><div class="sidebar-section-title">Modules</div>';
355
+
356
+ var groups={},order=[];
357
+ modules.forEach(function(mod){
358
+ var label=moduleLabel(mod.filePath,modules);
359
+ var slash=label.lastIndexOf('/');
360
+ var dir=slash===-1?'':label.slice(0,slash);
361
+ if(!groups[dir]){groups[dir]=[];order.push(dir);}
362
+ groups[dir].push(mod);
363
+ });
364
+ var hasGroups=order.some(function(d){return d!=='';});
365
+
366
+ order.forEach(function(dir){
367
+ var mods=groups[dir];
368
+ if(hasGroups&&dir){
369
+ var anyActive=mods.some(function(m){return moduleHtmlPath(m.filePath,modules)===activePath;});
370
+ html+='<details'+(anyActive?' open':'')+'><summary class="sidebar-dir-toggle">'+esc(dir)+'/</summary>';
371
+ mods.forEach(function(mod){
372
+ var rel=moduleHtmlPath(mod.filePath,modules);
373
+ var label=moduleLabel(mod.filePath,modules);
374
+ var name=label.slice(dir.length+1);
375
+ var active=activePath===rel?' active':'';
376
+ html+='<a class="sidebar-link sidebar-link-indent'+active+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
377
+ });
378
+ html+='</details>';
379
+ } else {
380
+ mods.forEach(function(mod){
381
+ var rel=moduleHtmlPath(mod.filePath,modules);
382
+ var label=moduleLabel(mod.filePath,modules);
383
+ var name=dir?label.slice(dir.length+1):label;
384
+ var active=activePath===rel?' active':'';
385
+ html+='<a class="sidebar-link'+active+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
386
+ });
387
+ }
388
+ });
389
+ return html+'</div>';
390
+ }
391
+
392
+ // ---------------------------------------------------------------------------
393
+ // Item renderers
394
+ // ---------------------------------------------------------------------------
395
+
396
+ function renderFunction(fn,filePath,sourceUrl,symbolMap){
397
+ var paramStr=(fn.params||[]).map(function(p){return(p.optional?'[':'')+p.name+': '+p.type+(p.optional?']':'');}).join(', ');
398
+ var sig=fn.name+'('+paramStr+'): '+fn.returnType;
399
+ var id=anchorId('fn',fn.name);
400
+ var badges=[fn.isExported&&badge('exported','exported'),fn.isAsync&&badge('async','async'),fn.isGenerator&&badge('generator','generator'),fn.deprecated!=null&&badge('deprecated','deprecated')].filter(Boolean).join('');
401
+ return'<div class="card" id="'+id+'">'
402
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(fn.name)+sourceLink(fn,filePath,sourceUrl)+'</div><div class="card-sig">'+esc(sig)+'</div></div>'+copyBtn(sig)+'</div>'
403
+ +'<div>'+badges+'</div>'+descHtml(fn,symbolMap,filePath)
404
+ +renderParams(fn.params,fn.jsdocParams)
405
+ +renderReturns(fn.returnType,fn.returns)
406
+ +renderThrows(fn.throws)
407
+ +'</div>';
408
+ }
409
+
410
+ function renderClass(cls,filePath,sourceUrl,symbolMap){
411
+ var id=anchorId('cls',cls.name);
412
+ var badges=[cls.isExported&&badge('exported','exported'),cls.isAbstract&&badge('abstract','abstract'),cls.extends.length&&badge('extends '+cls.extends.join(', '),'exported'),cls.implements.length&&badge('implements '+cls.implements.join(', '),'async'),cls.deprecated!=null&&badge('deprecated','deprecated')].filter(Boolean).join('');
413
+ var inner='<div class="card" id="'+id+'">'
414
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(cls.name)+sourceLink(cls,filePath,sourceUrl)+'</div></div>'
415
+ +copyBtn('class '+cls.name+(cls.extends.length?' extends '+cls.extends.join(', '):''))+'</div>'
416
+ +'<div>'+badges+'</div>'+descHtml(cls,symbolMap,filePath);
417
+
418
+ if(cls.constructor){
419
+ var ctorSig='new '+cls.name+'('+(cls.constructor.params||[]).map(function(p){return p.name+': '+p.type;}).join(', ')+')';
420
+ var ctorBody='<div class="method-row"><div class="method-sig">'+esc(ctorSig)+'</div>'+(cls.constructor.description?'<div class="method-desc">'+esc(cls.constructor.description)+'</div>':'')+renderParams(cls.constructor.params,cls.constructor.jsdocParams)+renderThrows(cls.constructor.throws)+'</div>';
421
+ inner+=collapsible('Constructor',ctorBody,true);
422
+ }
423
+ if(cls.properties.length){
424
+ var propBody='<table class="params-table"><thead><tr><th>Name</th><th>Type</th><th>Visibility</th><th>Flags</th><th>Description</th></tr></thead><tbody>';
425
+ cls.properties.forEach(function(p){
426
+ var flags=[p.isStatic&&badge('static','static'),p.isReadonly&&badge('readonly','readonly'),p.isAbstract&&badge('abstract','abstract'),p.deprecated!=null&&badge('deprecated','deprecated')].filter(Boolean).join('');
427
+ propBody+='<tr><td><code>'+esc(p.name)+'</code></td><td><code>'+esc(p.type)+'</code></td><td>'+badge(p.visibility,p.visibility)+'</td><td>'+flags+'</td><td>'+(p.description?esc(p.description):'')+'</td></tr>';
428
+ });
429
+ inner+=collapsible('Properties ('+cls.properties.length+')',propBody+'</tbody></table>',true);
430
+ }
431
+ if(cls.methods.length){
432
+ var methBody='';
433
+ cls.methods.forEach(function(m){
434
+ var ps=(m.params||[]).map(function(p){return p.name+': '+p.type;}).join(', ');
435
+ var mSig=m.name+'('+ps+'): '+m.returnType;
436
+ var mb=[badge(m.visibility,m.visibility),m.isStatic&&badge('static','static'),m.isAbstract&&badge('abstract','abstract'),m.isAsync&&badge('async','async'),m.isGenerator&&badge('generator','generator'),m.deprecated!=null&&badge('deprecated','deprecated')].filter(Boolean).join('');
437
+ methBody+='<div class="method-row"><div class="card-header" style="margin-bottom:4px"><code class="method-sig">'+esc(mSig)+'</code>'+copyBtn(mSig)+'</div><div>'+mb+'</div>'+(m.description?'<div class="method-desc">'+esc(m.description)+'</div>':'')+metaHtml(m)+renderParams(m.params,m.jsdocParams)+renderReturns(m.returnType,m.returns)+renderThrows(m.throws)+'</div>';
438
+ });
439
+ inner+=collapsible('Methods ('+cls.methods.length+')',methBody,true);
440
+ }
441
+ if(cls.getters.length||cls.setters.length){
442
+ var accBody='';
443
+ cls.getters.forEach(function(g){accBody+='<div class="method-row"><code class="method-sig">get '+esc(g.name)+'(): '+esc(g.returnType)+'</code>'+(g.isStatic?badge('static','static'):'')+(g.deprecated!=null?badge('deprecated','deprecated'):'')+(g.description?'<div class="method-desc">'+esc(g.description)+'</div>':'')+'</div>';});
444
+ cls.setters.forEach(function(s){var ps=(s.params||[]).map(function(p){return p.name+': '+p.type;}).join(', ');accBody+='<div class="method-row"><code class="method-sig">set '+esc(s.name)+'('+esc(ps)+')</code>'+(s.isStatic?badge('static','static'):'')+(s.deprecated!=null?badge('deprecated','deprecated'):'')+(s.description?'<div class="method-desc">'+esc(s.description)+'</div>':'')+'</div>';});
445
+ inner+=collapsible('Accessors ('+(cls.getters.length+cls.setters.length)+')',accBody,false);
446
+ }
447
+ return inner+'</div>';
448
+ }
449
+
450
+ function renderInterface(iface,filePath,sourceUrl,symbolMap){
451
+ var id=anchorId('iface',iface.name);
452
+ var html='<div class="card" id="'+id+'">'
453
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(iface.name)+sourceLink(iface,filePath,sourceUrl)+'</div></div>'+copyBtn('interface '+iface.name)+'</div>'
454
+ +(iface.isExported?badge('exported','exported'):'')+descHtml(iface,symbolMap,filePath);
455
+ if(iface.properties.length){
456
+ html+='<table class="params-table" style="margin-top:10px"><thead><tr><th>Property</th><th>Type</th><th>Optional</th></tr></thead><tbody>';
457
+ iface.properties.forEach(function(p){html+='<tr><td><code>'+esc(p.name)+'</code></td><td><code>'+esc(p.type)+'</code></td><td>'+(p.optional?'yes':'')+'</td></tr>';});
458
+ html+='</tbody></table>';
459
+ }
460
+ if(iface.methods.length){
461
+ var mb='';
462
+ iface.methods.forEach(function(m){var ps=m.params.map(function(p){return p.name+': '+p.type;}).join(', ');mb+='<div class="method-row"><code class="method-sig">'+esc(m.name)+'('+esc(ps)+'): '+esc(m.returnType)+'</code>'+(m.optional?badge('optional','optional'):'')+'</div>';});
463
+ html+=collapsible('Methods ('+iface.methods.length+')',mb,true);
464
+ }
465
+ return html+'</div>';
466
+ }
467
+
468
+ function renderEnum(enm,filePath,sourceUrl,symbolMap){
469
+ var id=anchorId('enum',enm.name);
470
+ var html='<div class="card" id="'+id+'">'
471
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(enm.name)+sourceLink(enm,filePath,sourceUrl)+'</div></div>'+copyBtn('enum '+enm.name)+'</div>'
472
+ +(enm.isExported?badge('exported','exported'):'')+descHtml(enm,symbolMap,filePath)
473
+ +'<table class="params-table" style="margin-top:10px"><thead><tr><th>Member</th><th>Value</th></tr></thead><tbody>';
474
+ enm.members.forEach(function(m){html+='<tr><td><code>'+esc(m.name)+'</code></td><td>'+(m.value!==null?'<code>'+esc(m.value)+'</code>':'')+'</td></tr>';});
475
+ return html+'</tbody></table></div>';
476
+ }
477
+
478
+ function renderTypeAlias(ta,filePath,sourceUrl,symbolMap){
479
+ var id=anchorId('type',ta.name);
480
+ return'<div class="card" id="'+id+'">'
481
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(ta.name)+sourceLink(ta,filePath,sourceUrl)+'</div><div class="card-sig">type '+esc(ta.name)+' = '+esc(ta.type)+'</div></div>'+copyBtn('type '+ta.name+' = '+ta.type)+'</div>'
482
+ +(ta.isExported?badge('exported','exported'):'')+descHtml(ta,symbolMap,filePath)+'</div>';
483
+ }
484
+
485
+ function renderVariable(v,filePath,sourceUrl,symbolMap){
486
+ var id=anchorId('var',v.name);
487
+ var decl=(v.isConst?'const':'let')+' '+v.name+': '+v.type;
488
+ return'<div class="card" id="'+id+'">'
489
+ +'<div class="card-header"><div><div class="card-name"><a class="anchor-link" href="#'+id+'">#</a>'+esc(v.name)+sourceLink(v,filePath,sourceUrl)+'</div><div class="card-sig">'+esc(decl)+'</div></div>'+copyBtn(decl)+'</div>'
490
+ +(v.isExported?badge('exported','exported'):'')+badge(v.isConst?'const':'var',v.isConst?'const':'var')+(v.deprecated!=null?badge('deprecated','deprecated'):'')+descHtml(v,symbolMap,filePath)+'</div>';
491
+ }
492
+
493
+ function section(title,items,renderFn,filePath,sourceUrl,symbolMap){
494
+ if(!items||!items.length) return '';
495
+ return'<div class="section"><div class="section-title">'+esc(title)+'</div>'+items.map(function(item){return renderFn(item,filePath,sourceUrl,symbolMap);}).join('\n')+'</div>';
496
+ }
497
+
498
+ // ---------------------------------------------------------------------------
499
+ // Site builder
500
+ // ---------------------------------------------------------------------------
501
+
502
+ function buildSite(modules,options){
503
+ options=options||{};
504
+ var projectName=options.projectName||'Documentation';
505
+ var version=options.version||'';
506
+ var theme=options.theme||'default';
507
+ var sourceUrl=options.sourceUrl||null;
508
+ // Build a symbol → {anchorId, modulePath} map for @link resolution
509
+ var symbolMap={};
510
+ modules.forEach(function(mod){
511
+ var rel=moduleHtmlPath(mod.filePath,modules);
512
+ function reg(name,aid){symbolMap[name]={anchorId:aid,modulePath:rel};}
513
+ mod.functions.forEach(function(f){reg(f.name,anchorId('fn',f.name));});
514
+ mod.classes.forEach(function(c){reg(c.name,anchorId('cls',c.name));c.methods.forEach(function(m){reg(c.name+'.'+m.name,anchorId('cls',c.name));});});
515
+ mod.interfaces.forEach(function(i){reg(i.name,anchorId('iface',i.name));});
516
+ mod.typeAliases.forEach(function(t){reg(t.name,anchorId('type',t.name));});
517
+ mod.enums.forEach(function(e){reg(e.name,anchorId('enum',e.name));});
518
+ mod.variables.forEach(function(v){reg(v.name,anchorId('var',v.name));});
519
+ });
520
+ var showToggle=(THEMES[theme]||THEMES.default).toggleBtn;
521
+ var pages=[];
522
+ var idxIdx=buildSearchIndex(modules,'');
523
+ var modIdx=buildSearchIndex(modules,'../');
524
+
525
+ // index.html
526
+ var sidebar=buildSidebar(modules,projectName,version,'index.html','',showToggle);
527
+ var totalFns=modules.reduce(function(s,m){return s+m.functions.length;},0);
528
+ var totalCls=modules.reduce(function(s,m){return s+m.classes.length;},0);
529
+ var body='<div class="page-title">'+esc(projectName)+'</div>'
530
+ +'<div class="page-subtitle">'+modules.length+' module(s) &middot; '+totalFns+' function(s) &middot; '+totalCls+' class(es)</div>'
531
+ +'<div class="section"><div class="section-title">Modules</div><div class="module-grid">';
532
+ modules.forEach(function(mod){
533
+ var rel=moduleHtmlPath(mod.filePath,modules);
534
+ var label=moduleLabel(mod.filePath,modules);
535
+ var total=mod.functions.length+mod.classes.length+mod.interfaces.length+mod.typeAliases.length+mod.enums.length+mod.variables.length;
536
+ var parts=[mod.functions.length&&mod.functions.length+' fn',mod.classes.length&&mod.classes.length+' class',mod.interfaces.length&&mod.interfaces.length+' iface',mod.enums.length&&mod.enums.length+' enum',mod.variables.length&&mod.variables.length+' const'].filter(Boolean);
537
+ // deprecated count across all items
538
+ var depCount=[].concat(mod.functions,mod.classes,mod.interfaces,mod.typeAliases,mod.enums,mod.variables).filter(function(i){return i.deprecated!=null;}).length;
539
+ // @since version range
540
+ var sinces=[].concat(mod.functions,mod.classes,mod.interfaces,mod.typeAliases,mod.enums,mod.variables).map(function(i){return i.since;}).filter(Boolean);
541
+ var sinceStr=sinces.length?(' · since v'+sinces.sort()[0])+(sinces.length>1&&sinces[sinces.length-1]!==sinces[0]?'–v'+sinces[sinces.length-1]:''):''
542
+ var depStr=depCount?'<span class="badge badge-deprecated" style="font-size:10px;padding:1px 5px;vertical-align:middle">'+depCount+' deprecated</span>':'';
543
+ var desc=mod.description?'<div class="module-card-desc">'+esc(mod.description.slice(0,100))+(mod.description.length>100?'…':'')+'</div>':'';
544
+ body+='<a class="module-card" href="'+esc(rel)+'"><div class="module-card-name">'+esc(label)+depStr+'</div><div class="module-card-stats">'+(parts.join(' · ')||'no exported items')+esc(sinceStr)+'</div>'+desc+'</a>';
545
+ });
546
+ body+='</div></div>';
547
+ pages.push({path:'index.html',html:page(projectName,sidebar,body,idxIdx,theme)});
548
+
549
+ // per-module pages
550
+ modules.forEach(function(mod){
551
+ var rel=moduleHtmlPath(mod.filePath,modules);
552
+ var label=moduleLabel(mod.filePath,modules);
553
+ var modSidebar=buildSidebar(modules,projectName,version,rel,'../',showToggle);
554
+ var mbody='<div class="breadcrumb"><a href="../index.html">'+esc(projectName)+'</a> / '+esc(label)+'</div>'
555
+ +'<div class="page-title">'+esc(label)+'</div>'
556
+ +'<div class="page-subtitle" style="font-family:monospace;font-size:12px">'+esc(mod.filePath)+'</div>'
557
+ +(mod.description?'<p class="module-desc">'+esc(mod.description)+'</p>':'');
558
+ var isEmpty=!mod.functions.length&&!mod.classes.length&&!mod.interfaces.length&&!mod.typeAliases.length&&!mod.enums.length&&!mod.variables.length;
559
+ if(isEmpty){
560
+ mbody+='<p class="empty" style="margin-top:24px">No documented items found.</p>';
561
+ } else {
562
+ mbody+=section('Functions',mod.functions,renderFunction,mod.filePath,sourceUrl,symbolMap);
563
+ mbody+=section('Classes',mod.classes,renderClass,mod.filePath,sourceUrl,symbolMap);
564
+ mbody+=section('Interfaces',mod.interfaces,renderInterface,mod.filePath,sourceUrl,symbolMap);
565
+ mbody+=section('Type Aliases',mod.typeAliases,renderTypeAlias,mod.filePath,sourceUrl,symbolMap);
566
+ mbody+=section('Enums',mod.enums,renderEnum,mod.filePath,sourceUrl,symbolMap);
567
+ mbody+=section('Variables & Constants',mod.variables,renderVariable,mod.filePath,sourceUrl,symbolMap);
568
+ }
569
+ pages.push({path:rel,html:page(label+' - '+projectName,modSidebar,mbody,modIdx,theme)});
570
+ });
571
+
572
+ return pages;
573
+ }
574
+
575
+ module.exports = { buildSite, moduleLabel, moduleHtmlPath };