jsdoc-scribe 1.0.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,831 @@
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:#555;--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 — layout, components, responsive, print
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const CSS_STRUCTURE = `
32
+ /* == Reset & base == */
33
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
34
+ html{scroll-behavior:smooth}
35
+ 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}
36
+ a{color:var(--accent);text-decoration:none}
37
+ a:hover{text-decoration:underline}
38
+ code,pre{font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace}
39
+
40
+ /* == Three-column layout == */
41
+ .layout{display:grid;grid-template-columns:272px 1fr;min-height:100vh}
42
+ .layout.has-toc{grid-template-columns:272px 1fr 224px}
43
+
44
+ /* == Left sidebar == */
45
+ .sidebar{grid-column:1;background:var(--sidebar-bg);color:var(--sidebar-text);position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column;border-right:1px solid rgba(255,255,255,.06)}
46
+ .sidebar-header{padding:16px 14px 12px;display:flex;align-items:flex-start;justify-content:space-between;gap:8px;border-bottom:1px solid var(--search-border);flex-shrink:0}
47
+ .sidebar-header a{color:var(--sidebar-text);font-size:14px;font-weight:700;letter-spacing:-.01em}
48
+ .sidebar-header .version{display:block;font-size:11px;color:var(--sidebar-title);margin-top:2px;font-family:monospace}
49
+ .theme-btn{background:none;border:1px solid var(--search-border);border-radius:4px;padding:2px 7px;font-size:11px;color:var(--sidebar-text);cursor:pointer;white-space:nowrap;flex-shrink:0;margin-top:2px}
50
+ .theme-btn:hover{background:var(--sidebar-active)}
51
+
52
+ /* == Search == */
53
+ .search-wrap{position:relative;padding:10px 12px;border-bottom:1px solid var(--search-border);flex-shrink:0}
54
+ .search-icon{position:absolute;left:20px;top:50%;transform:translateY(-50%);width:14px;height:14px;opacity:.5;pointer-events:none}
55
+ .search-box{width:100%;background:var(--search-bg);border:1px solid var(--search-border);border-radius:6px;padding:6px 10px 6px 30px;color:var(--search-text);font-size:13px;outline:none}
56
+ .search-box::placeholder{color:var(--sidebar-title)}
57
+ .search-box:focus{border-color:var(--accent)}
58
+ .search-results{display:none;position:absolute;left:12px;right:12px;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;box-shadow:0 4px 20px rgba(0,0,0,.18)}
59
+ .search-results.visible{display:block}
60
+ .search-result-item{display:block;padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--search-border);text-decoration:none}
61
+ .search-result-item:hover{background:var(--sidebar-active)}
62
+ .sr-name{font-size:13px;font-weight:600;color:var(--search-text);font-family:monospace}
63
+ .sr-kind{font-size:11px;color:var(--sidebar-title);margin-left:6px}
64
+ .sr-module{font-size:11px;color:var(--sidebar-title);display:block;margin-top:1px;opacity:.7}
65
+ .search-no-results{padding:10px 12px;color:var(--sidebar-title);font-size:13px}
66
+ .sr-preview{font-size:11px;color:var(--text3);font-style:italic;display:block;margin-top:2px}
67
+
68
+ /* == Sidebar tree == */
69
+ .sidebar-section{padding:4px 0 12px;flex:1;overflow-y:auto}
70
+ .sidebar-section-title{padding:10px 14px 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--sidebar-title)}
71
+ .sidebar-link{display:block;padding:4px 14px;font-size:13px;color:var(--sidebar-text);transition:background .1s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
72
+ .sidebar-link:hover{background:var(--sidebar-active);color:var(--text);text-decoration:none}
73
+ .sidebar-link.active{background:var(--sidebar-active);color:var(--text);text-decoration:none;border-left:2px solid var(--accent);padding-left:12px}
74
+ .sidebar-dir-toggle{display:flex;align-items:center;gap:5px;cursor:pointer;list-style:none;padding:4px 14px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--sidebar-title);user-select:none}
75
+ .sidebar-dir-toggle::-webkit-details-marker{display:none}
76
+ .sidebar-dir-toggle::before{content:'▶';font-size:8px;transition:transform .15s;flex-shrink:0}
77
+ details[open] .sidebar-dir-toggle::before{transform:rotate(90deg)}
78
+ .sidebar-link-indent{padding-left:26px}
79
+ .sidebar-link-indent.active{padding-left:24px}
80
+
81
+ /* Symbol rows under active module */
82
+ .sym-rows{padding:2px 0 6px}
83
+ .sym-row{display:flex;align-items:center;gap:5px;padding:2px 12px 2px 36px;min-width:0}
84
+ .sym-pill{font-size:9px;font-weight:700;padding:1px 4px;border-radius:3px;font-family:monospace;flex-shrink:0;letter-spacing:.01em;line-height:1.4}
85
+ .sym-fn{background:#1b3a1e;color:#81c784}
86
+ .sym-cls{background:#0d2137;color:#64b5f6}
87
+ .sym-iface{background:#1a0a2e;color:#ce93d8}
88
+ .sym-enum{background:#2d1500;color:#ffb74d}
89
+ .sym-type{background:#00211f;color:#4db6ac}
90
+ .sym-var{background:#1f1f1f;color:#9e9e9e}
91
+ [data-theme=light] .sym-fn,[data-theme=default] .sym-fn,.sym-fn{background:#1b3a1e;color:#81c784}
92
+ .sym-link{font-size:12px;color:var(--sidebar-text);text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;opacity:.85}
93
+ .sym-link:hover{color:var(--text);opacity:1;text-decoration:none}
94
+ .sym-link.active{color:var(--accent);opacity:1;font-weight:600}
95
+
96
+ /* == Main content area == */
97
+ .main{padding:40px 48px 80px;min-width:0;overflow-x:hidden}
98
+ .page-title{font-size:26px;font-weight:700;color:var(--text);margin-bottom:4px;letter-spacing:-.02em}
99
+ .page-subtitle{color:var(--text2);font-size:12px;margin-bottom:16px;font-family:monospace}
100
+ .module-desc{color:var(--text2);font-size:14px;line-height:1.7;margin-bottom:28px;max-width:700px}
101
+ .section{margin-bottom:44px}
102
+ .section-title{font-size:16px;font-weight:700;color:var(--text);margin-bottom:14px;padding-bottom:8px;border-bottom:2px solid var(--accent2);display:flex;align-items:center;gap:8px}
103
+ .section-count{font-size:12px;font-weight:400;color:var(--text3);font-family:monospace}
104
+
105
+ /* == Cards == */
106
+ .card{background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--border);border-radius:8px;padding:18px 20px 16px;margin-bottom:10px;scroll-margin-top:24px;transition:box-shadow .15s,border-left-color .15s}
107
+ .card:hover{box-shadow:0 2px 12px rgba(0,0,0,.08)}
108
+ .card-fn{border-left-color:#43a047}
109
+ .card-cls{border-left-color:#1e88e5}
110
+ .card-iface{border-left-color:#8e24aa}
111
+ .card-enum{border-left-color:#fb8c00}
112
+ .card-type{border-left-color:#00897b}
113
+ .card-var{border-left-color:#757575}
114
+ .card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;margin-bottom:2px}
115
+ .card-name{font-size:15px;font-weight:700;color:var(--text);font-family:monospace;display:flex;align-items:center;gap:4px;flex-wrap:wrap}
116
+ .card-sig{font-size:12px;color:var(--text2);margin-top:3px;font-family:monospace;word-break:break-all;line-height:1.5}
117
+ .card-desc{font-size:13px;color:var(--text2);margin-top:8px;line-height:1.6}
118
+ .card-example{margin-top:12px}
119
+ .card-example pre{background:var(--code-bg);border:1px solid var(--border);border-radius:6px;padding:12px 16px;font-size:12px;overflow-x:auto;color:var(--text);line-height:1.6;tab-size:2}
120
+ .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}
121
+ .copy-btn:hover{background:var(--accent2);border-color:var(--accent);color:var(--accent)}
122
+ .copy-btn.copied{background:#e8f5e9;border-color:#43a047;color:#2e7d32}
123
+
124
+ /* == Badges == */
125
+ .badge{display:inline-block;padding:2px 7px;border-radius:4px;font-size:11px;font-weight:600;margin-right:3px;margin-top:5px}
126
+ .badge-exported{background:#e8f5e9;color:#2e7d32}
127
+ .badge-async{background:#e3f2fd;color:#1565c0}
128
+ .badge-abstract{background:#fce4ec;color:#c62828}
129
+ .badge-static{background:#fff3e0;color:#e65100}
130
+ .badge-readonly{background:#f3e5f5;color:#6a1b9a}
131
+ .badge-generator{background:#e0f2f1;color:#00695c}
132
+ .badge-private{background:#fafafa;color:#666;border:1px solid #ddd}
133
+ .badge-protected{background:#fff8e1;color:#f57f17}
134
+ .badge-public{background:#f5f5f5;color:#555}
135
+ .badge-optional{background:#f3e5f5;color:#6a1b9a}
136
+ .badge-const{background:#ede7f6;color:#4527a0}
137
+ .badge-var{background:#fff3e0;color:#e65100}
138
+ .badge-deprecated{background:var(--dep-bg);color:var(--dep-text)}
139
+ .badge-since{background:#e8f5e9;color:#2e7d32}
140
+ .deprecated-notice{background:var(--dep-bg);color:var(--dep-text);border-radius:5px;padding:6px 12px;font-size:12px;margin-top:8px}
141
+ .since-label{font-size:11px;color:var(--text3);margin-top:4px}
142
+
143
+ /* == Tables == */
144
+ .throws-table{width:100%;border-collapse:collapse;margin-top:8px;font-size:13px;border:1px solid var(--throws-bg)}
145
+ .throws-table th{text-align:left;padding:5px 10px;background:var(--throws-bg);color:var(--throws-text);font-weight:600;font-size:12px}
146
+ .throws-table td{padding:5px 10px;border-top:1px solid var(--border);color:var(--text);vertical-align:top}
147
+ .throws-table td code{font-size:12px}
148
+ .params-table{width:100%;border-collapse:collapse;margin-top:10px;font-size:13px}
149
+ .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)}
150
+ .params-table td{padding:6px 10px;border-bottom:1px solid var(--method-border);vertical-align:top;color:var(--text)}
151
+ .params-table td code{background:var(--code-bg);padding:1px 5px;border-radius:3px;font-size:12px}
152
+ .returns{margin-top:8px;font-size:13px;color:var(--text2)}
153
+ .returns code{background:var(--code-bg);padding:1px 6px;border-radius:3px;font-family:monospace}
154
+
155
+ /* == Collapsible sections == */
156
+ .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}
157
+ .collapse-toggle::-webkit-details-marker{display:none}
158
+ .collapse-toggle::before{content:'▶';font-size:9px;display:inline-block;transition:transform .15s;color:var(--sub-label)}
159
+ details[open] .collapse-toggle::before{transform:rotate(90deg)}
160
+ .collapse-body{margin-top:4px}
161
+ .method-row{margin-top:8px;padding:10px 0;border-top:1px solid var(--method-border)}
162
+ .method-sig{font-family:monospace;font-size:13px;color:var(--text)}
163
+ .method-desc{font-size:12px;color:var(--text2);margin-top:4px}
164
+
165
+ /* == Module index grid == */
166
+ .module-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:10px;margin-top:4px}
167
+ .module-card{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:16px 20px;transition:border-color .15s,box-shadow .15s;display:block}
168
+ .module-card:hover{border-color:var(--accent);box-shadow:0 2px 12px rgba(67,97,238,.1);text-decoration:none}
169
+ .module-card-name{font-size:14px;font-weight:700;color:var(--text);font-family:monospace;display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:4px}
170
+ .module-card-stats{font-size:12px;color:var(--text3)}
171
+ .module-card-desc{font-size:12px;color:var(--text3);margin-top:6px;line-height:1.4}
172
+
173
+ /* == Breadcrumb == */
174
+ .breadcrumb{font-size:13px;color:var(--text3);margin-bottom:20px}
175
+ .breadcrumb a{color:var(--accent)}
176
+
177
+ /* == Misc == */
178
+ .anchor-link{color:var(--sidebar-title);opacity:0;font-size:13px;margin-left:6px;transition:opacity .15s}
179
+ .card:hover .anchor-link{opacity:.6}
180
+ .anchor-link:hover{opacity:1;text-decoration:none}
181
+ .empty{color:var(--text3);font-size:13px;font-style:italic}
182
+ .link-ref{color:var(--accent);text-decoration:none;font-size:inherit}
183
+ .link-ref:hover{text-decoration:underline}
184
+ .source-link{font-size:11px;color:var(--text3);font-family:monospace;text-decoration:none;margin-left:6px;opacity:.7}
185
+ .source-link:hover{opacity:1;text-decoration:underline;color:var(--accent)}
186
+
187
+ /* == Right TOC == */
188
+ .toc{grid-column:3;position:sticky;top:0;height:100vh;overflow-y:auto;padding:40px 16px 40px 20px;border-left:1px solid var(--border);background:var(--bg)}
189
+ .toc-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--text3);margin-bottom:14px}
190
+ .toc-section{margin-bottom:14px}
191
+ .toc-section-label{font-size:11px;font-weight:600;color:var(--text2);margin-bottom:4px;padding-bottom:3px;border-bottom:1px solid var(--border)}
192
+ .toc-item{display:block;font-size:12px;color:var(--text3);text-decoration:none;padding:3px 0 3px 8px;border-left:2px solid transparent;transition:color .1s,border-left-color .1s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.5}
193
+ .toc-item:hover{color:var(--text);border-left-color:var(--border);text-decoration:none}
194
+ .toc-item.active{color:var(--accent);border-left-color:var(--accent);font-weight:600}
195
+ .toc-dep{font-size:9px;color:var(--dep-text);margin-left:3px;font-weight:700;vertical-align:middle}
196
+
197
+ /* == Hamburger (mobile only) == */
198
+ .hamburger{display:none;flex-direction:column;gap:5px;background:var(--sidebar-bg);border:none;padding:8px 10px;cursor:pointer;position:fixed;top:10px;left:10px;z-index:300;border-radius:6px;box-shadow:0 2px 8px rgba(0,0,0,.2)}
199
+ .hamburger span{width:20px;height:2px;background:var(--sidebar-text);border-radius:2px;display:block;transition:all .2s}
200
+ .hamburger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
201
+ .hamburger.open span:nth-child(2){opacity:0}
202
+ .hamburger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
203
+
204
+ /* == Code token highlighting == */
205
+ .tok-kw{color:#6b8cff;font-weight:600}
206
+ .tok-str{color:#2e7d32}
207
+ .tok-cmt{color:#888;font-style:italic}
208
+ .tok-num{color:#e65100}
209
+ .tok-type{color:#6a1b9a}
210
+ [data-theme=dark] .tok-kw{color:#89b4ff}
211
+ [data-theme=dark] .tok-str{color:#a8d5a2}
212
+ [data-theme=dark] .tok-cmt{color:#5a6494}
213
+ [data-theme=dark] .tok-num{color:#ffb74d}
214
+ [data-theme=dark] .tok-type{color:#ce93d8}
215
+
216
+ /* == Responsive == */
217
+ @media (max-width:1280px){
218
+ .toc{display:none}
219
+ .layout.has-toc{grid-template-columns:272px 1fr}
220
+ }
221
+ @media (max-width:860px){
222
+ .layout,.layout.has-toc{grid-template-columns:1fr}
223
+ .sidebar{position:fixed;top:0;left:0;height:100vh;z-index:200;transform:translateX(-100%);transition:transform .25s ease}
224
+ .sidebar.open{transform:translateX(0);box-shadow:4px 0 24px rgba(0,0,0,.28)}
225
+ .hamburger{display:flex}
226
+ .main{padding:56px 20px 60px}
227
+ }
228
+
229
+ /* == Print == */
230
+ @media print{
231
+ .sidebar,.toc,.hamburger,.copy-btn,.theme-btn,.search-wrap,.anchor-link{display:none!important}
232
+ .layout,.layout.has-toc{grid-template-columns:1fr!important;display:block}
233
+ .main{padding:0}
234
+ .card{break-inside:avoid;border:1px solid #ccc;border-left:3px solid #999!important;margin-bottom:16px;box-shadow:none}
235
+ }
236
+ `;
237
+
238
+ // ---------------------------------------------------------------------------
239
+ // App JS (written to assets/app.js)
240
+ // ---------------------------------------------------------------------------
241
+
242
+ const CLIENT_JS = `
243
+ (function(){
244
+ var _pfx=(window.location.pathname.replace(/\\/g,'/').indexOf('/modules/')!==-1)?'../':'';
245
+ // Theme
246
+ var THEME_KEY='jsdoc-scribe-theme';
247
+ var saved=localStorage.getItem(THEME_KEY);
248
+ if(saved) document.documentElement.setAttribute('data-theme',saved);
249
+ var btn=document.getElementById('theme-btn');
250
+ if(btn){
251
+ btn.textContent=(saved==='dark')?'Light':'Dark';
252
+ btn.addEventListener('click',function(){
253
+ var cur=document.documentElement.getAttribute('data-theme');
254
+ var next=cur==='dark'?'light':'dark';
255
+ document.documentElement.setAttribute('data-theme',next);
256
+ localStorage.setItem(THEME_KEY,next);
257
+ btn.textContent=next==='dark'?'Light':'Dark';
258
+ });
259
+ }
260
+ // Copy button
261
+ document.addEventListener('click',function(e){
262
+ var b=e.target.closest('.copy-btn');
263
+ if(!b) return;
264
+ navigator.clipboard.writeText(b.dataset.sig||'').then(function(){
265
+ b.textContent='Copied!';b.classList.add('copied');
266
+ setTimeout(function(){b.textContent='Copy';b.classList.remove('copied');},1500);
267
+ });
268
+ });
269
+ // Search
270
+ var INDEX=(window.__SEARCH_INDEX__||[]).map(function(r){return{name:r.name,kind:r.kind,module:r.module,body:r.body,url:_pfx+r.url};});
271
+ var box=document.getElementById('search-box');
272
+ var panel=document.getElementById('search-results');
273
+ if(box&&panel){
274
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
275
+ function render(items){
276
+ panel.innerHTML=items.length
277
+ ?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('')
278
+ :'<div class="search-no-results">No results</div>';
279
+ panel.classList.add('visible');
280
+ }
281
+ function search(q){
282
+ q=q.trim().toLowerCase();
283
+ if(!q){panel.classList.remove('visible');return;}
284
+ render(INDEX.filter(function(r){
285
+ return r.name.toLowerCase().includes(q)||r.module.toLowerCase().includes(q)||(r.body&&r.body.toLowerCase().includes(q));
286
+ }));
287
+ }
288
+ box.addEventListener('input',function(){search(box.value);});
289
+ box.addEventListener('focus',function(){if(box.value.trim())search(box.value);});
290
+ document.addEventListener('click',function(e){if(!box.contains(e.target)&&!panel.contains(e.target))panel.classList.remove('visible');});
291
+ document.addEventListener('keydown',function(e){
292
+ if((e.metaKey||e.ctrlKey)&&e.key==='k'){e.preventDefault();box.focus();box.select();}
293
+ if(e.key==='Escape')panel.classList.remove('visible');
294
+ });
295
+ }
296
+ // Right TOC scroll spy via IntersectionObserver
297
+ var toc=document.getElementById('toc');
298
+ if(toc&&typeof IntersectionObserver!=='undefined'){
299
+ var cards=document.querySelectorAll('.card[id]');
300
+ var tocLinks={};
301
+ toc.querySelectorAll('[data-anchor]').forEach(function(a){tocLinks[a.dataset.anchor]=a;});
302
+ var current='';
303
+ var obs=new IntersectionObserver(function(entries){
304
+ entries.forEach(function(e){
305
+ if(e.isIntersecting){
306
+ if(current&&tocLinks[current])tocLinks[current].classList.remove('active');
307
+ current=e.target.id;
308
+ if(tocLinks[current])tocLinks[current].classList.add('active');
309
+ }
310
+ });
311
+ },{rootMargin:'-10% 0px -70% 0px',threshold:0});
312
+ cards.forEach(function(c){obs.observe(c);});
313
+ }
314
+ // Mobile hamburger
315
+ var hamburger=document.getElementById('hamburger');
316
+ var sidebar=document.querySelector('.sidebar');
317
+ if(hamburger&&sidebar){
318
+ hamburger.addEventListener('click',function(){
319
+ var open=sidebar.classList.toggle('open');
320
+ hamburger.classList.toggle('open',open);
321
+ });
322
+ document.addEventListener('click',function(e){
323
+ if(sidebar.classList.contains('open')&&!sidebar.contains(e.target)&&!hamburger.contains(e.target)){
324
+ sidebar.classList.remove('open');
325
+ hamburger.classList.remove('open');
326
+ }
327
+ });
328
+ }
329
+ })();
330
+ `;
331
+
332
+ // ---------------------------------------------------------------------------
333
+ // Helpers
334
+ // ---------------------------------------------------------------------------
335
+
336
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
337
+ function badge(label,cls){return'<span class="badge badge-'+cls+'">'+esc(label)+'</span>';}
338
+ function anchorId(kind,name){return kind+'-'+name.replace(/[^a-zA-Z0-9_]/g,'_');}
339
+ function copyBtn(sig){return'<button class="copy-btn" data-sig="'+esc(sig)+'" title="Copy">Copy</button>';}
340
+
341
+ function metaHtml(item){
342
+ var out='';
343
+ if(item.deprecated!=null) out+='<div class="deprecated-notice">&#9888; Deprecated'+(item.deprecated?': '+esc(item.deprecated):'')+'</div>';
344
+ if(item.since) out+='<div class="since-label">Since v'+esc(item.since)+'</div>';
345
+ return out;
346
+ }
347
+
348
+ /**
349
+ * Server-side tokenizer for JS/TS @example blocks.
350
+ * Returns HTML with tok-* spans. Processes strings, comments, numbers, keywords.
351
+ */
352
+ function highlightCode(raw){
353
+ if(!raw) return '';
354
+ var out='';
355
+ var KW=/\b(function|class|const|let|var|return|new|if|else|for|while|import|export|from|async|await|try|catch|throw|extends|implements|type|interface|enum|this|super|null|undefined|true|false|typeof|instanceof|of|in|do|switch|case|default|break|continue|static|readonly|abstract|private|protected|public|override|declare|namespace|as|satisfies)\b/g;
356
+ var re=/(\/\/[^\n]*)|(\/\*[\s\S]*?\*\/)|(["'`])(?:(?!\3)[^\\]|\\.)*\3|\b(\d+\.?\d*(?:[eE][+-]?\d+)?)\b/g;
357
+ var last=0;
358
+ var m;
359
+ while((m=re.exec(raw))!==null){
360
+ if(m.index>last){
361
+ // Tokenize the plain segment for keywords
362
+ out+=esc(raw.slice(last,m.index)).replace(KW,function(kw){return'<span class="tok-kw">'+kw+'</span>';});
363
+ }
364
+ if(m[1]||m[2]) out+='<span class="tok-cmt">'+esc(m[0])+'</span>';
365
+ else if(m[3]) out+='<span class="tok-str">'+esc(m[0])+'</span>';
366
+ else if(m[4]) out+='<span class="tok-num">'+esc(m[0])+'</span>';
367
+ else out+=esc(m[0]);
368
+ last=m.index+m[0].length;
369
+ }
370
+ if(last<raw.length){
371
+ out+=esc(raw.slice(last)).replace(KW,function(kw){return'<span class="tok-kw">'+kw+'</span>';});
372
+ }
373
+ return out;
374
+ }
375
+
376
+ function descHtml(item,symbolMap,filePath){
377
+ var out='';
378
+ if(item.description) out+='<div class="card-desc">'+(symbolMap?resolveLinks(item.description,symbolMap,filePath):esc(item.description))+'</div>';
379
+ out+=metaHtml(item);
380
+ if(item.example) out+='<div class="card-example"><pre>'+highlightCode(item.example)+'</pre></div>';
381
+ return out;
382
+ }
383
+
384
+ function renderParams(params, jsdocParams){
385
+ if(!params||!params.length) return '';
386
+ var jmap={};
387
+ (jsdocParams||[]).forEach(function(p){jmap[p.name]=p;});
388
+ var html='<table class="params-table"><thead><tr><th>Parameter</th><th>Type</th><th>Optional</th><th>Description</th></tr></thead><tbody>';
389
+ params.forEach(function(p){
390
+ var jp=jmap[p.name]||{};
391
+ var type=jp.type&&jp.type!=='any'?jp.type:p.type;
392
+ 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>';
393
+ });
394
+ return html+'</tbody></table>';
395
+ }
396
+
397
+ function renderReturns(returnType, returnsTag){
398
+ var type=returnsTag&&returnsTag.type&&returnsTag.type!=='any'?returnsTag.type:returnType;
399
+ var desc=returnsTag&&returnsTag.description?'&mdash; '+esc(returnsTag.description):'';
400
+ return'<div class="returns">Returns: <code>'+esc(type)+'</code> '+desc+'</div>';
401
+ }
402
+
403
+ function renderThrows(throws){
404
+ if(!throws||!throws.length) return '';
405
+ var html='<table class="throws-table"><thead><tr><th>Throws</th><th>Description</th></tr></thead><tbody>';
406
+ throws.forEach(function(t){html+='<tr><td><code>'+esc(t.type||'Error')+'</code></td><td>'+esc(t.description||'')+'</td></tr>';});
407
+ return html+'</tbody></table>';
408
+ }
409
+
410
+ function resolveLinks(text, symbolMap, filePath, moduleHtmlPathFn, modules){
411
+ if(!text||!symbolMap) return esc(text);
412
+ return esc(text).replace(/\{@link ([^}]+)\}/g, function(_, ref){
413
+ var parts=ref.trim().split('#');
414
+ var sym=parts[0].trim();
415
+ var method=parts[1]?parts[1].trim():null;
416
+ var entry=symbolMap[sym];
417
+ if(!entry) return'<code>'+esc(ref)+'</code>';
418
+ var targetPath=entry.modulePath;
419
+ var href=targetPath===filePath?'':(targetPath||'');
420
+ if(method) href+='#meth-'+sym+'_'+method;
421
+ else href+=('#'+entry.anchorId);
422
+ return'<a href="'+href+'" class="link-ref"><code>'+esc(sym+(method?'.'+method:''))+'</code></a>';
423
+ });
424
+ }
425
+
426
+ function sourceLink(item, filePath, sourceUrl){
427
+ if(item.line==null) return '';
428
+ var label='line '+item.line;
429
+ if(sourceUrl){
430
+ var base=sourceUrl.replace(/\/$/,'');
431
+ var rel=filePath.replace(/\\/g,'/');
432
+ var href=base+'/'+rel+'#L'+item.line;
433
+ return'<a class="source-link" href="'+esc(href)+'" target="_blank" rel="noopener">'+esc(label)+'</a>';
434
+ }
435
+ return'<span class="source-link">'+esc(filePath.replace(/\\/g,'/'))+':'+item.line+'</span>';
436
+ }
437
+
438
+ function collapsible(label,html,open){
439
+ return'<details'+(open?' open':'')+'><summary class="collapse-toggle">'+esc(label)+'</summary><div class="collapse-body">'+html+'</div></details>';
440
+ }
441
+
442
+ function buildCss(theme){
443
+ var t=THEMES[theme]||THEMES.default;
444
+ return t.light+(t.dark||'')+CSS_STRUCTURE;
445
+ }
446
+
447
+ function page(title,sidebarHtml,bodyHtml,theme,assetPrefix,tocHtml){
448
+ var p=assetPrefix||'';
449
+ var hasToc=!!tocHtml;
450
+ 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<link rel="stylesheet" href="'+p+'assets/style.css">\n</head>\n<body>\n<button id="hamburger" class="hamburger" aria-label="Menu"><span></span><span></span><span></span></button>\n<div class="layout'+(hasToc?' has-toc':'')+'">\n<nav class="sidebar">'+sidebarHtml+'</nav>\n<main class="main">'+bodyHtml+'</main>\n'+(hasToc?'<aside class="toc" id="toc">'+tocHtml+'</aside>\n':'')+'</div>\n<script src="'+p+'search-index.js"></script>\n<script src="'+p+'assets/app.js"></script>\n</body>\n</html>';
451
+ }
452
+
453
+ // ---------------------------------------------------------------------------
454
+ // Module label helpers
455
+ // ---------------------------------------------------------------------------
456
+
457
+ function commonRoot(modules){
458
+ if(!modules.length) return '';
459
+ var parts=modules.map(function(m){return m.filePath.replace(/\\/g,'/').split('/');});
460
+ var min=Math.min.apply(null,parts.map(function(p){return p.length;}))-1;
461
+ var i=0;
462
+ while(i<min&&parts.every(function(p){return p[i]===parts[0][i];})) i++;
463
+ return parts[0].slice(0,i).join('/');
464
+ }
465
+
466
+ function moduleLabel(filePath,modules){
467
+ var root=commonRoot(modules);
468
+ var rel=filePath.replace(/\\/g,'/');
469
+ if(root) rel=rel.slice(root.length).replace(/^\//,'');
470
+ return rel.replace(/\.[jt]sx?$/,'');
471
+ }
472
+
473
+ function moduleHtmlPath(filePath,modules){return'modules/'+moduleLabel(filePath,modules).replace(/\//g,'__')+'.html';}
474
+
475
+ // ---------------------------------------------------------------------------
476
+ // Search index
477
+ // ---------------------------------------------------------------------------
478
+
479
+ function buildBody(item){
480
+ var parts=[];
481
+ if(item.description) parts.push(item.description);
482
+ (item.jsdocParams||[]).forEach(function(p){if(p.description)parts.push(p.name+': '+p.description);});
483
+ if(item.returns&&item.returns.description) parts.push('returns: '+item.returns.description);
484
+ (item.throws||[]).forEach(function(t){if(t.description)parts.push('throws: '+t.description);});
485
+ return parts.join(' | ').slice(0,300)||null;
486
+ }
487
+
488
+ function buildSearchIndex(modules,prefix){
489
+ prefix=prefix||'';
490
+ var index=[];
491
+ modules.forEach(function(mod){
492
+ var rel=moduleHtmlPath(mod.filePath,modules);
493
+ var label=moduleLabel(mod.filePath,modules);
494
+ function url(a){return prefix+rel+'#'+a;}
495
+ function push(name,kind,anchor,item){index.push({name:name,kind:kind,module:label,url:url(anchor),body:buildBody(item||{})});}
496
+ mod.functions.forEach(function(f){push(f.name,'function',anchorId('fn',f.name),f);});
497
+ mod.classes.forEach(function(c){
498
+ push(c.name,'class',anchorId('cls',c.name),c);
499
+ c.methods.forEach(function(m){push(c.name+'.'+m.name,'method',anchorId('cls',c.name),m);});
500
+ });
501
+ mod.interfaces.forEach(function(i){push(i.name,'interface',anchorId('iface',i.name),i);});
502
+ mod.typeAliases.forEach(function(t){push(t.name,'type',anchorId('type',t.name),t);});
503
+ mod.enums.forEach(function(e){push(e.name,'enum',anchorId('enum',e.name),e);});
504
+ mod.variables.forEach(function(v){push(v.name,v.isConst?'const':'var',anchorId('var',v.name),v);});
505
+ });
506
+ return index;
507
+ }
508
+
509
+ // ---------------------------------------------------------------------------
510
+ // Sidebar (with symbol tree under active module)
511
+ // ---------------------------------------------------------------------------
512
+
513
+ function buildSidebar(modules,projectName,version,activePath,rootPrefix,showToggle,activeModule){
514
+ var prefix=rootPrefix||'';
515
+ var html='<div class="sidebar-header">'
516
+ +'<div><a href="'+prefix+'index.html">'+esc(projectName)+'</a>'+(version?'<span class="version">v'+esc(version)+'</span>':'')+'</div>'
517
+ +(showToggle!==false?'<button id="theme-btn" class="theme-btn">Dark</button>':'')
518
+ +'</div>'
519
+ +'<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>'
520
+ +'<div class="sidebar-section"><div class="sidebar-section-title">Modules</div>';
521
+
522
+ var groups={},order=[];
523
+ modules.forEach(function(mod){
524
+ var label=moduleLabel(mod.filePath,modules);
525
+ var slash=label.lastIndexOf('/');
526
+ var dir=slash===-1?'':label.slice(0,slash);
527
+ if(!groups[dir]){groups[dir]=[];order.push(dir);}
528
+ groups[dir].push(mod);
529
+ });
530
+ var hasGroups=order.some(function(d){return d!=='';});
531
+
532
+ function symRows(mod){
533
+ var rows='<div class="sym-rows">';
534
+ var specs=[
535
+ {list:mod.functions,kind:'fn',label:'fn'},
536
+ {list:mod.classes,kind:'cls',label:'cls'},
537
+ {list:mod.interfaces,kind:'iface',label:'if'},
538
+ {list:mod.typeAliases,kind:'type',label:'ty'},
539
+ {list:mod.enums,kind:'enum',label:'en'},
540
+ {list:mod.variables,kind:'var',label:'$'},
541
+ ];
542
+ specs.forEach(function(s){
543
+ (s.list||[]).forEach(function(item){
544
+ var anchor=anchorId(s.kind,item.name);
545
+ rows+='<div class="sym-row"><span class="sym-pill sym-'+s.kind+'">'+s.label+'</span>'
546
+ +'<a class="sym-link" href="#'+anchor+'">'+esc(item.name)+'</a></div>';
547
+ });
548
+ });
549
+ return rows+'</div>';
550
+ }
551
+
552
+ order.forEach(function(dir){
553
+ var mods=groups[dir];
554
+ if(hasGroups&&dir){
555
+ var anyActive=mods.some(function(m){return moduleHtmlPath(m.filePath,modules)===activePath;});
556
+ html+='<details'+(anyActive?' open':'')+'><summary class="sidebar-dir-toggle">'+esc(dir)+'/</summary>';
557
+ mods.forEach(function(mod){
558
+ var rel=moduleHtmlPath(mod.filePath,modules);
559
+ var label=moduleLabel(mod.filePath,modules);
560
+ var name=label.slice(dir.length+1);
561
+ var isActive=activePath===rel;
562
+ html+='<a class="sidebar-link sidebar-link-indent'+(isActive?' active':'')+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
563
+ if(isActive&&activeModule) html+=symRows(activeModule);
564
+ });
565
+ html+='</details>';
566
+ } else {
567
+ mods.forEach(function(mod){
568
+ var rel=moduleHtmlPath(mod.filePath,modules);
569
+ var label=moduleLabel(mod.filePath,modules);
570
+ var name=dir?label.slice(dir.length+1):label;
571
+ var isActive=activePath===rel;
572
+ html+='<a class="sidebar-link'+(isActive?' active':'')+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
573
+ if(isActive&&activeModule) html+=symRows(activeModule);
574
+ });
575
+ }
576
+ });
577
+ return html+'</div>';
578
+ }
579
+
580
+ // ---------------------------------------------------------------------------
581
+ // Right TOC builder
582
+ // ---------------------------------------------------------------------------
583
+
584
+ function buildToc(mod){
585
+ var specs=[
586
+ {title:'Functions',list:mod.functions,kind:'fn'},
587
+ {title:'Classes',list:mod.classes,kind:'cls'},
588
+ {title:'Interfaces',list:mod.interfaces,kind:'iface'},
589
+ {title:'Type Aliases',list:mod.typeAliases,kind:'type'},
590
+ {title:'Enums',list:mod.enums,kind:'enum'},
591
+ {title:'Variables',list:mod.variables,kind:'var'},
592
+ ].filter(function(s){return s.list&&s.list.length;});
593
+ if(!specs.length) return '';
594
+ var html='<div class="toc-title">On this page</div>';
595
+ specs.forEach(function(s){
596
+ html+='<div class="toc-section"><div class="toc-section-label">'+esc(s.title)+'</div>';
597
+ s.list.forEach(function(item){
598
+ var anchor=anchorId(s.kind,item.name);
599
+ var dep=item.deprecated!=null?'<span class="toc-dep">dep</span>':'';
600
+ html+='<a class="toc-item" href="#'+anchor+'" data-anchor="'+anchor+'">'+esc(item.name)+dep+'</a>';
601
+ });
602
+ html+='</div>';
603
+ });
604
+ return html;
605
+ }
606
+
607
+ // ---------------------------------------------------------------------------
608
+ // Item renderers
609
+ // ---------------------------------------------------------------------------
610
+
611
+ function renderFunction(fn,filePath,sourceUrl,symbolMap){
612
+ var paramStr=(fn.params||[]).map(function(p){return(p.optional?'[':'')+p.name+': '+p.type+(p.optional?']':'');}).join(', ');
613
+ var sig=fn.name+'('+paramStr+'): '+fn.returnType;
614
+ var id=anchorId('fn',fn.name);
615
+ 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('');
616
+ return'<div class="card card-fn" id="'+id+'">'
617
+ +'<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>'
618
+ +'<div>'+badges+'</div>'+descHtml(fn,symbolMap,filePath)
619
+ +renderParams(fn.params,fn.jsdocParams)
620
+ +renderReturns(fn.returnType,fn.returns)
621
+ +renderThrows(fn.throws)
622
+ +'</div>';
623
+ }
624
+
625
+ function renderClass(cls,filePath,sourceUrl,symbolMap){
626
+ var id=anchorId('cls',cls.name);
627
+ 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('');
628
+ var inner='<div class="card card-cls" id="'+id+'">'
629
+ +'<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>'
630
+ +copyBtn('class '+cls.name+(cls.extends.length?' extends '+cls.extends.join(', '):''))+'</div>'
631
+ +'<div>'+badges+'</div>'+descHtml(cls,symbolMap,filePath);
632
+
633
+ if(cls.constructor){
634
+ var ctorSig='new '+cls.name+'('+(cls.constructor.params||[]).map(function(p){return p.name+': '+p.type;}).join(', ')+')';
635
+ 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>';
636
+ inner+=collapsible('Constructor',ctorBody,true);
637
+ }
638
+ if(cls.properties.length){
639
+ 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>';
640
+ cls.properties.forEach(function(p){
641
+ 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('');
642
+ 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>';
643
+ });
644
+ inner+=collapsible('Properties ('+cls.properties.length+')',propBody+'</tbody></table>',true);
645
+ }
646
+ if(cls.methods.length){
647
+ var methBody='';
648
+ cls.methods.forEach(function(m){
649
+ var ps=(m.params||[]).map(function(p){return p.name+': '+p.type;}).join(', ');
650
+ var mSig=m.name+'('+ps+'): '+m.returnType;
651
+ 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('');
652
+ 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>';
653
+ });
654
+ inner+=collapsible('Methods ('+cls.methods.length+')',methBody,true);
655
+ }
656
+ if(cls.getters.length||cls.setters.length){
657
+ var accBody='';
658
+ 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>';});
659
+ 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>';});
660
+ inner+=collapsible('Accessors ('+(cls.getters.length+cls.setters.length)+')',accBody,false);
661
+ }
662
+ return inner+'</div>';
663
+ }
664
+
665
+ function renderInterface(iface,filePath,sourceUrl,symbolMap){
666
+ var id=anchorId('iface',iface.name);
667
+ var html='<div class="card card-iface" id="'+id+'">'
668
+ +'<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>'
669
+ +(iface.isExported?badge('exported','exported'):'')+descHtml(iface,symbolMap,filePath);
670
+ if(iface.properties.length){
671
+ html+='<table class="params-table" style="margin-top:10px"><thead><tr><th>Property</th><th>Type</th><th>Optional</th></tr></thead><tbody>';
672
+ 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>';});
673
+ html+='</tbody></table>';
674
+ }
675
+ if(iface.methods.length){
676
+ var mb='';
677
+ 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>';});
678
+ html+=collapsible('Methods ('+iface.methods.length+')',mb,true);
679
+ }
680
+ return html+'</div>';
681
+ }
682
+
683
+ function renderEnum(enm,filePath,sourceUrl,symbolMap){
684
+ var id=anchorId('enum',enm.name);
685
+ var html='<div class="card card-enum" id="'+id+'">'
686
+ +'<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>'
687
+ +(enm.isExported?badge('exported','exported'):'')+descHtml(enm,symbolMap,filePath)
688
+ +'<table class="params-table" style="margin-top:10px"><thead><tr><th>Member</th><th>Value</th></tr></thead><tbody>';
689
+ 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>';});
690
+ return html+'</tbody></table></div>';
691
+ }
692
+
693
+ function renderTypeAlias(ta,filePath,sourceUrl,symbolMap){
694
+ var id=anchorId('type',ta.name);
695
+ return'<div class="card card-type" id="'+id+'">'
696
+ +'<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>'
697
+ +(ta.isExported?badge('exported','exported'):'')+descHtml(ta,symbolMap,filePath)+'</div>';
698
+ }
699
+
700
+ function renderVariable(v,filePath,sourceUrl,symbolMap){
701
+ var id=anchorId('var',v.name);
702
+ var decl=(v.isConst?'const':'let')+' '+v.name+': '+v.type;
703
+ return'<div class="card card-var" id="'+id+'">'
704
+ +'<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>'
705
+ +(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>';
706
+ }
707
+
708
+ function section(title,items,renderFn,filePath,sourceUrl,symbolMap){
709
+ if(!items||!items.length) return '';
710
+ var count='<span class="section-count">('+items.length+')</span>';
711
+ return'<div class="section"><div class="section-title">'+esc(title)+count+'</div>'+items.map(function(item){return renderFn(item,filePath,sourceUrl,symbolMap);}).join('\n')+'</div>';
712
+ }
713
+
714
+ // ---------------------------------------------------------------------------
715
+ // Site builder — helpers
716
+ // ---------------------------------------------------------------------------
717
+
718
+ /** Build the symbol map for @link cross-reference resolution. */
719
+ function buildSymbolMap(modules){
720
+ var map={};
721
+ modules.forEach(function(mod){
722
+ var rel=moduleHtmlPath(mod.filePath,modules);
723
+ function reg(name,aid){map[name]={anchorId:aid,modulePath:rel};}
724
+ mod.functions.forEach(function(f){reg(f.name,anchorId('fn',f.name));});
725
+ mod.classes.forEach(function(c){
726
+ reg(c.name,anchorId('cls',c.name));
727
+ c.methods.forEach(function(m){reg(c.name+'.'+m.name,anchorId('cls',c.name));});
728
+ });
729
+ mod.interfaces.forEach(function(i){reg(i.name,anchorId('iface',i.name));});
730
+ mod.typeAliases.forEach(function(t){reg(t.name,anchorId('type',t.name));});
731
+ mod.enums.forEach(function(e){reg(e.name,anchorId('enum',e.name));});
732
+ mod.variables.forEach(function(v){reg(v.name,anchorId('var',v.name));});
733
+ });
734
+ return map;
735
+ }
736
+
737
+ /** Build the module-grid HTML for the index page. */
738
+ function buildIndexBody(modules){
739
+ var body='<div class="section"><div class="section-title">Modules</div><div class="module-grid">';
740
+ modules.forEach(function(mod){
741
+ var rel=moduleHtmlPath(mod.filePath,modules);
742
+ var label=moduleLabel(mod.filePath,modules);
743
+ var parts=[
744
+ mod.functions.length&&mod.functions.length+' fn',
745
+ mod.classes.length&&mod.classes.length+' class',
746
+ mod.interfaces.length&&mod.interfaces.length+' iface',
747
+ mod.enums.length&&mod.enums.length+' enum',
748
+ mod.variables.length&&mod.variables.length+' const',
749
+ ].filter(Boolean);
750
+ var allItems=[].concat(mod.functions,mod.classes,mod.interfaces,mod.typeAliases,mod.enums,mod.variables);
751
+ var depCount=allItems.filter(function(i){return i.deprecated!=null;}).length;
752
+ var sinces=allItems.map(function(i){return i.since;}).filter(Boolean).sort();
753
+ var sinceStr=sinces.length
754
+ ? (' · since v'+sinces[0]+(sinces.length>1&&sinces[sinces.length-1]!==sinces[0]?'–v'+sinces[sinces.length-1]:''))
755
+ : '';
756
+ var depBadge=depCount?'<span class="badge badge-deprecated" style="font-size:10px;padding:1px 5px">'+depCount+' dep</span>':'';
757
+ var descHtml=mod.description?'<div class="module-card-desc">'+esc(mod.description.slice(0,100))+(mod.description.length>100?'…':'')+'</div>':'';
758
+ body+='<a class="module-card" href="'+esc(rel)+'">';
759
+ body+='<div class="module-card-name">'+esc(label)+depBadge+'</div>';
760
+ body+='<div class="module-card-stats">'+(parts.join(' · ')||'no exported items')+esc(sinceStr)+'</div>';
761
+ body+=descHtml+'</a>';
762
+ });
763
+ return body+'</div></div>';
764
+ }
765
+
766
+ /** Build a single module page body (sections only, no header). */
767
+ function buildModuleBody(mod,sourceUrl,symbolMap){
768
+ var isEmpty=!mod.functions.length&&!mod.classes.length&&!mod.interfaces.length
769
+ &&!mod.typeAliases.length&&!mod.enums.length&&!mod.variables.length;
770
+ if(isEmpty) return '<p class="empty" style="margin-top:24px">No documented items found.</p>';
771
+ var body='';
772
+ body+=section('Functions',mod.functions,renderFunction,mod.filePath,sourceUrl,symbolMap);
773
+ body+=section('Classes',mod.classes,renderClass,mod.filePath,sourceUrl,symbolMap);
774
+ body+=section('Interfaces',mod.interfaces,renderInterface,mod.filePath,sourceUrl,symbolMap);
775
+ body+=section('Type Aliases',mod.typeAliases,renderTypeAlias,mod.filePath,sourceUrl,symbolMap);
776
+ body+=section('Enums',mod.enums,renderEnum,mod.filePath,sourceUrl,symbolMap);
777
+ body+=section('Variables & Constants',mod.variables,renderVariable,mod.filePath,sourceUrl,symbolMap);
778
+ return body;
779
+ }
780
+
781
+ // ---------------------------------------------------------------------------
782
+ // Site builder
783
+ // ---------------------------------------------------------------------------
784
+
785
+ function buildSite(modules,options){
786
+ options=options||{};
787
+ var projectName=options.projectName||'Documentation';
788
+ var version=options.version||'';
789
+ var theme=options.theme||'default';
790
+ var sourceUrl=options.sourceUrl||null;
791
+ var symbolMap=buildSymbolMap(modules);
792
+ var showToggle=(THEMES[theme]||THEMES.default).toggleBtn;
793
+ var pages=[];
794
+
795
+ // Shared static assets
796
+ pages.push({path:'assets/style.css', html:buildCss(theme)});
797
+ pages.push({path:'assets/app.js', html:CLIENT_JS});
798
+ pages.push({path:'search-index.js', html:'window.__SEARCH_INDEX__='+JSON.stringify(buildSearchIndex(modules,''))+';'});
799
+
800
+ // Index page
801
+ var totalFns=modules.reduce(function(s,m){return s+m.functions.length;},0);
802
+ var totalCls=modules.reduce(function(s,m){return s+m.classes.length;},0);
803
+ var idxHeader='<div class="page-title">'+esc(projectName)+'</div>'
804
+ +'<div class="page-subtitle">'+modules.length+' module(s) &middot; '+totalFns+' function(s) &middot; '+totalCls+' class(es)</div>';
805
+ pages.push({
806
+ path:'index.html',
807
+ html:page(projectName,buildSidebar(modules,projectName,version,'index.html','',showToggle,null),idxHeader+buildIndexBody(modules),theme,'',null),
808
+ });
809
+
810
+ // Per-module pages
811
+ modules.forEach(function(mod){
812
+ var rel=moduleHtmlPath(mod.filePath,modules);
813
+ var label=moduleLabel(mod.filePath,modules);
814
+ var modHeader='<div class="breadcrumb"><a href="../index.html">'+esc(projectName)+'</a> / '+esc(label)+'</div>'
815
+ +'<div class="page-title">'+esc(label)+'</div>'
816
+ +'<div class="page-subtitle">'+esc(mod.filePath)+'</div>'
817
+ +(mod.description?'<p class="module-desc">'+esc(mod.description)+'</p>':'');
818
+ pages.push({
819
+ path:rel,
820
+ html:page(
821
+ label+' - '+projectName,
822
+ buildSidebar(modules,projectName,version,rel,'../',showToggle,mod),
823
+ modHeader+buildModuleBody(mod,sourceUrl,symbolMap),
824
+ theme,'../',buildToc(mod)
825
+ ),
826
+ });
827
+ });
828
+
829
+ return pages;
830
+ }
831
+ module.exports = { buildSite, moduleLabel, moduleHtmlPath };