jsdoc-scribe 1.7.0 → 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.
- package/CHANGELOG.md +132 -0
- package/README.md +32 -11
- package/lib/docs.js +23 -37
- package/lib/extractor.js +12 -0
- package/lib/renderer.js +391 -135
- package/package.json +56 -56
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,138 @@
|
|
|
3
3
|
All notable changes to `jsdoc-scribe` are documented here.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
5
5
|
|
|
6
|
+
## [1.11.0] - 2026-06-30
|
|
7
|
+
|
|
8
|
+
### Changed — Code Quality (Phase D)
|
|
9
|
+
|
|
10
|
+
**`extractor.js` robustness**
|
|
11
|
+
- Added `sourceFile` null guard: if `ts.createSourceFile()` returns a falsy value the function logs to stderr and returns an empty-but-valid module object instead of throwing.
|
|
12
|
+
- Wrapped the entire `visit(node)` body in `try/catch`: a malformed or unexpected AST node now logs a warning (`jsdoc-scribe: skipped node in <file> — <message>`) and continues visiting sibling nodes instead of aborting the whole module.
|
|
13
|
+
|
|
14
|
+
**`renderer.js` refactor**
|
|
15
|
+
- Extracted `buildSymbolMap(modules)` from `buildSite()` — builds the `{name → {anchorId, modulePath}}` cross-reference map used by `{@link}` resolution.
|
|
16
|
+
- Extracted `buildIndexBody(modules)` — builds the module-grid HTML block for the index page (previously inlined in `buildSite()`).
|
|
17
|
+
- Extracted `buildModuleBody(mod, sourceUrl, symbolMap)` — builds the sections HTML for a single module page (previously inlined in `buildSite()`).
|
|
18
|
+
- `buildSite()` is now a slim coordinator (~25 lines) delegating to the three helpers above.
|
|
19
|
+
|
|
20
|
+
**`docs.js` async upgrade**
|
|
21
|
+
- `extractModules(files)` is now `async` and uses `Promise.allSettled`: all files are attempted in parallel; fulfilled results are collected, rejected ones are logged to stderr and skipped.
|
|
22
|
+
- `generateSite(inputPaths, options)` is now `async` accordingly.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [1.10.0] - 2026-06-30
|
|
27
|
+
|
|
28
|
+
### Added — Test coverage (Phase C)
|
|
29
|
+
|
|
30
|
+
Expanded `npm test` from 7 tests to **25 tests** across three suites.
|
|
31
|
+
|
|
32
|
+
**`test/extractor.test.js` — 10 new tests** (exercises `lib/extractor.js`):
|
|
33
|
+
- Module-level `@module` description and `@since` extracted from top-of-file JSDoc
|
|
34
|
+
- `@param` type and description parsed from JSDoc block
|
|
35
|
+
- `@returns` type and description parsed
|
|
36
|
+
- `@since` and `@deprecated` on individual items
|
|
37
|
+
- `@throws` with type and description
|
|
38
|
+
- 1-based source line numbers on all functions (ordered)
|
|
39
|
+
- Class with constructor, methods, properties, and static members
|
|
40
|
+
- Interface with optional properties
|
|
41
|
+
- Enum members and their values
|
|
42
|
+
- Type alias and exported `const` variable
|
|
43
|
+
|
|
44
|
+
**`test/renderer.test.js` — 8 new tests** (exercises `lib/renderer.js` via mock module objects):
|
|
45
|
+
- `buildSite` returns exactly 3 shared assets + `index.html` + one page per module
|
|
46
|
+
- Search index contains every symbol (function, class) with root-relative URLs
|
|
47
|
+
- Module page includes right-side TOC with `data-anchor` attributes and "On this page" title
|
|
48
|
+
- `@deprecated` badge and notice rendered for deprecated items
|
|
49
|
+
- Source link contains GitHub URL and `#L42` line anchor when `sourceUrl` set
|
|
50
|
+
- `@example` blocks rendered with `tok-kw` syntax-highlighting spans
|
|
51
|
+
- Sidebar symbol tree includes `sym-rows` and kind pills for the active module
|
|
52
|
+
- `{@link Symbol}` in descriptions resolves to `<a class="link-ref">` with `href`
|
|
53
|
+
|
|
54
|
+
### Fixed — `@description` tag in JSDoc blocks
|
|
55
|
+
|
|
56
|
+
`parseJSDocBlock()` now recognises `@description <text>` as an alias for the plain-text
|
|
57
|
+
description that precedes the first `@tag`. Previously `@description` was silently discarded as
|
|
58
|
+
an unknown tag, leaving `mod.description === null` for any file that used it (including all
|
|
59
|
+
built-in sample files).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## [1.9.0] - 2026-06-30
|
|
64
|
+
|
|
65
|
+
### Changed — Enterprise HTML Redesign (Phase B)
|
|
66
|
+
|
|
67
|
+
Complete visual overhaul of the generated documentation site.
|
|
68
|
+
|
|
69
|
+
**Three-column layout**
|
|
70
|
+
- Left sidebar (272 px) · Center content (flexible) · Right TOC (224 px)
|
|
71
|
+
- CSS Grid replaces the old flexbox layout: `.layout { grid-template-columns: 272px 1fr 224px }`
|
|
72
|
+
- Index page uses two-column grid (no right TOC); module pages use three columns automatically via the `has-toc` class
|
|
73
|
+
|
|
74
|
+
**Right-side "On this page" TOC**
|
|
75
|
+
- New `buildToc(mod)` function generates a per-section TOC (Functions, Classes, Interfaces, etc.) with every symbol as a clickable anchor
|
|
76
|
+
- `IntersectionObserver` scroll spy in `app.js` tracks which card is on screen and highlights the matching TOC item with an accent-colored left border
|
|
77
|
+
- TOC hides at ≤1280 px viewport width; three columns collapse to two
|
|
78
|
+
|
|
79
|
+
**Symbol tree in sidebar**
|
|
80
|
+
- Active module expands to show every symbol underneath its file link
|
|
81
|
+
- Each symbol is prefixed with a color-coded pill badge (`fn`, `cls`, `if`, `ty`, `en`, `$`) matching its kind
|
|
82
|
+
- New `.sym-rows`, `.sym-row`, `.sym-pill`, `.sym-link` CSS classes
|
|
83
|
+
|
|
84
|
+
**Color-coded cards**
|
|
85
|
+
- Each card now has a 3 px left accent border: green=function, blue=class, purple=interface, orange=enum, teal=type alias, gray=variable/const
|
|
86
|
+
- Added `card-fn`, `card-cls`, `card-iface`, `card-enum`, `card-type`, `card-var` CSS classes to all render functions
|
|
87
|
+
|
|
88
|
+
**Server-side syntax highlighting for `@example` blocks**
|
|
89
|
+
- New `highlightCode(raw)` function in `renderer.js` tokenizes JS/TS code at build time
|
|
90
|
+
- Handles line comments, block comments, strings (single/double/template), numbers, and 40+ keywords
|
|
91
|
+
- Produces `<span class="tok-kw|tok-str|tok-cmt|tok-num">` spans; colors adapt to dark/light theme via CSS vars
|
|
92
|
+
|
|
93
|
+
**Responsive layout**
|
|
94
|
+
- `@media (max-width: 1280px)`: right TOC hidden, grid collapses to two columns
|
|
95
|
+
- `@media (max-width: 860px)`: sidebar becomes a fixed overlay (off-screen by default), hamburger button appears in the top-left, main content uses mobile padding
|
|
96
|
+
- Hamburger toggle with animated open/close icon (three-bar → X)
|
|
97
|
+
|
|
98
|
+
**Print styles**
|
|
99
|
+
- `@media print`: sidebar, TOC, hamburger, copy buttons, theme toggle, and search box all hidden
|
|
100
|
+
- Cards get `break-inside: avoid` and a neutral border for clean PDF output
|
|
101
|
+
|
|
102
|
+
**Section counts**
|
|
103
|
+
- Section headings now show item count: `Functions (3)` using a `.section-count` monospace chip
|
|
104
|
+
|
|
105
|
+
**Other polish**
|
|
106
|
+
- `html { scroll-behavior: smooth }` for smooth anchor navigation
|
|
107
|
+
- Sidebar active link gets a 2 px accent left border instead of just a background change
|
|
108
|
+
- Card hover adds a subtle box-shadow
|
|
109
|
+
- All `section()` calls updated with count display
|
|
110
|
+
- CSS ~15 KB (up from ~9 KB), app.js ~4 KB (up from ~2.6 KB) — still cached after first load
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## [1.8.0] - 2026-06-30
|
|
115
|
+
|
|
116
|
+
### Changed — Performance: Shared Static Assets (Phase A)
|
|
117
|
+
|
|
118
|
+
Previously every generated HTML page inlined the same 9 KB CSS block, 3 KB client JS, and 34 KB search index. On a 9-page site that wasted 630+ KB of duplicate payload.
|
|
119
|
+
|
|
120
|
+
- **CSS extracted** to `assets/style.css` — written once per build, shared by all pages via `<link rel="stylesheet">`.
|
|
121
|
+
- **Client JS extracted** to `assets/app.js` — written once, shared via `<script src>`. Browsers cache it after the first page load.
|
|
122
|
+
- **Search index extracted** to `search-index.js` — written once as `window.__SEARCH_INDEX__=[...]`, loaded before `app.js` via `<script src>`. No more 34 KB inline JSON on every page.
|
|
123
|
+
- `app.js` auto-detects its location (`/modules/` in the path) and adjusts search result URLs at runtime, so a single shared index file works for both the index page and all module pages.
|
|
124
|
+
|
|
125
|
+
### Result
|
|
126
|
+
| Metric | Before | After |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| Total site size | 628 KB | 225 KB (−64%) |
|
|
129
|
+
| Per-page HTML (avg) | ~70 KB | ~20 KB |
|
|
130
|
+
| Cache benefit (2nd+ page) | ~70 KB reload | ~20 KB reload |
|
|
131
|
+
| Shared assets (loaded once) | — | 46 KB |
|
|
132
|
+
|
|
133
|
+
### Upgraded — `buildSite()` output
|
|
134
|
+
The return array now includes three additional entries: `{ path: 'assets/style.css', html: '...' }`, `{ path: 'assets/app.js', html: '...' }`, `{ path: 'search-index.js', html: '...' }`. The CLI handles these automatically. Programmatic API users should use `fs.mkdirSync(path.dirname(dest), { recursive: true })` before writing each file (the README example is updated).
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
6
138
|
## [1.7.0] - 2026-06-29
|
|
7
139
|
|
|
8
140
|
### Added
|
package/README.md
CHANGED
|
@@ -230,7 +230,20 @@ gen-docs src --out docs --title "My Project"
|
|
|
230
230
|
# Open docs/index.html in your browser
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
The output is a self-contained static site — no server required.
|
|
233
|
+
The output is a self-contained static site — no server required. Output structure:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
docs/
|
|
237
|
+
index.html # project index (module cards)
|
|
238
|
+
search-index.js # shared search index (fetched once)
|
|
239
|
+
assets/
|
|
240
|
+
style.css # shared CSS — cached after first page load
|
|
241
|
+
app.js # shared JS (search, theme toggle, copy)
|
|
242
|
+
modules/
|
|
243
|
+
api.html
|
|
244
|
+
utils.html
|
|
245
|
+
...
|
|
246
|
+
```
|
|
234
247
|
|
|
235
248
|
### CLI flags
|
|
236
249
|
|
|
@@ -349,13 +362,19 @@ const {
|
|
|
349
362
|
|
|
350
363
|
```js
|
|
351
364
|
const { generateSite } = require('jsdoc-scribe/docs');
|
|
365
|
+
const fs = require('fs'), path = require('path');
|
|
352
366
|
|
|
353
|
-
generateSite(['src'], {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
theme: 'default',
|
|
357
|
-
sourceUrl: 'https://github.com/org/repo/blob/main',
|
|
367
|
+
const pages = generateSite(['src'], {
|
|
368
|
+
projectName: 'My Project',
|
|
369
|
+
version: '1.0.0',
|
|
358
370
|
});
|
|
371
|
+
|
|
372
|
+
// pages = [{ path: 'index.html', html }, { path: 'assets/style.css', html }, ...]
|
|
373
|
+
for (const p of pages) {
|
|
374
|
+
const dest = path.join('docs', p.path);
|
|
375
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
376
|
+
fs.writeFileSync(dest, p.html, 'utf8');
|
|
377
|
+
}
|
|
359
378
|
```
|
|
360
379
|
|
|
361
380
|
### Step-by-step
|
|
@@ -390,13 +409,15 @@ const pages = buildSite(modules, {
|
|
|
390
409
|
sourceUrl: 'https://github.com/org/repo/blob/main',
|
|
391
410
|
});
|
|
392
411
|
|
|
393
|
-
// 5. Write
|
|
412
|
+
// 5. Write all files
|
|
413
|
+
// pages = [{ path, html }] and includes HTML pages + shared assets:
|
|
414
|
+
// assets/style.css, assets/app.js, search-index.js
|
|
415
|
+
// Always use mkdirSync so assets/ and modules/ are created automatically.
|
|
394
416
|
const outDir = 'docs';
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const dest = path.join(outDir, filePath);
|
|
417
|
+
for (const p of pages) {
|
|
418
|
+
const dest = path.join(outDir, p.path);
|
|
398
419
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
399
|
-
fs.writeFileSync(dest, html);
|
|
420
|
+
fs.writeFileSync(dest, p.html, 'utf8');
|
|
400
421
|
}
|
|
401
422
|
```
|
|
402
423
|
|
package/lib/docs.js
CHANGED
|
@@ -1,71 +1,57 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* jsdoc-scribe/docs — programmatic API
|
|
5
|
-
*
|
|
6
|
-
* Use this when you want to drive jsdoc-scribe from Node code rather than
|
|
7
|
-
* the CLI. All lower-level pieces are also exported so you can build a
|
|
8
|
-
* custom renderer on top of the same extraction pipeline.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* const { extractModule, buildSite, collectFiles } = require('jsdoc-scribe/docs');
|
|
12
|
-
*
|
|
13
|
-
* const files = collectFiles('./src');
|
|
14
|
-
* const modules = files.map(f => extractModule(f));
|
|
15
|
-
* const pages = buildSite(modules, { projectName: 'My API', version: '1.0.0' });
|
|
16
|
-
*
|
|
17
|
-
* // pages is [{ path: 'index.html', html: '...' }, { path: 'modules/...html', html: '...' }]
|
|
18
|
-
* pages.forEach(p => require('fs').writeFileSync(require('path').join('./docs', p.path), p.html));
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
3
|
const { collectFiles, DEFAULT_EXTENSIONS, DEFAULT_IGNORE_DIRS } = require("./index.js");
|
|
22
4
|
const { extractModule } = require("./extractor.js");
|
|
23
5
|
const { buildSite, moduleLabel, moduleHtmlPath } = require("./renderer.js");
|
|
24
6
|
|
|
25
7
|
/**
|
|
26
|
-
* Extract
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
8
|
+
* Extract documentation models from an array of file paths.
|
|
9
|
+
* All files are attempted; failures are logged to stderr and omitted from results.
|
|
10
|
+
* Uses Promise.allSettled so a single bad file never aborts the batch.
|
|
11
|
+
*
|
|
12
|
+
* @param {string[]} files
|
|
13
|
+
* @returns {Promise<object[]>}
|
|
30
14
|
*/
|
|
31
|
-
function extractModules(files) {
|
|
15
|
+
async function extractModules(files) {
|
|
16
|
+
const results = await Promise.allSettled(
|
|
17
|
+
files.map(f => Promise.resolve().then(() => extractModule(f)))
|
|
18
|
+
);
|
|
32
19
|
const modules = [];
|
|
33
|
-
for (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
20
|
+
for (let i = 0; i < results.length; i++) {
|
|
21
|
+
const r = results[i];
|
|
22
|
+
if (r.status === "fulfilled") {
|
|
23
|
+
modules.push(r.value);
|
|
24
|
+
} else {
|
|
25
|
+
process.stderr.write("jsdoc-scribe/docs: skipped " + files[i] + ": " + r.reason.message + "\n");
|
|
38
26
|
}
|
|
39
27
|
}
|
|
40
28
|
return modules;
|
|
41
29
|
}
|
|
42
30
|
|
|
43
31
|
/**
|
|
44
|
-
* One-shot: collect files, extract docs, build site
|
|
45
|
-
*
|
|
46
|
-
* @param {
|
|
47
|
-
* @
|
|
32
|
+
* One-shot convenience: collect files, extract docs, build HTML site.
|
|
33
|
+
*
|
|
34
|
+
* @param {string|string[]} inputPaths Source file or directory paths
|
|
35
|
+
* @param {object} [options] Same options as buildSite()
|
|
36
|
+
* @returns {Promise<Array<{path:string,html:string}>>}
|
|
48
37
|
*/
|
|
49
|
-
function generateSite(inputPaths, options) {
|
|
38
|
+
async function generateSite(inputPaths, options) {
|
|
50
39
|
const paths = Array.isArray(inputPaths) ? inputPaths : [inputPaths];
|
|
51
40
|
const opts = options || {};
|
|
52
41
|
const files = [].concat(...paths.map(p => collectFiles(p, opts.extensions, opts.ignoreDirs)));
|
|
53
42
|
const unique = [...new Set(files)];
|
|
54
|
-
const modules = extractModules(unique);
|
|
43
|
+
const modules = await extractModules(unique);
|
|
55
44
|
return buildSite(modules, { projectName: opts.projectName, version: opts.version });
|
|
56
45
|
}
|
|
57
46
|
|
|
58
47
|
module.exports = {
|
|
59
|
-
// Core pipeline
|
|
60
48
|
collectFiles,
|
|
61
49
|
extractModule,
|
|
62
50
|
extractModules,
|
|
63
51
|
buildSite,
|
|
64
52
|
generateSite,
|
|
65
|
-
// Helpers
|
|
66
53
|
moduleLabel,
|
|
67
54
|
moduleHtmlPath,
|
|
68
|
-
// Constants
|
|
69
55
|
DEFAULT_EXTENSIONS,
|
|
70
56
|
DEFAULT_IGNORE_DIRS,
|
|
71
57
|
};
|
package/lib/extractor.js
CHANGED
|
@@ -159,6 +159,10 @@ function parseJSDocBlock(raw) {
|
|
|
159
159
|
} else if (/^@deprecated\b/.test(line)) {
|
|
160
160
|
inExample = false;
|
|
161
161
|
deprecated = line.slice("@deprecated".length).trim() || "";
|
|
162
|
+
} else if (/^@description\b/.test(line)) {
|
|
163
|
+
inExample = false;
|
|
164
|
+
const descText = line.slice("@description".length).trim();
|
|
165
|
+
if (descText) descLines.push(descText);
|
|
162
166
|
} else if (/^@\w/.test(line)) {
|
|
163
167
|
inExample = false;
|
|
164
168
|
} else if (inExample) {
|
|
@@ -395,10 +399,15 @@ function extractModule(filePath) {
|
|
|
395
399
|
const sourceText = fs.readFileSync(filePath, "utf8");
|
|
396
400
|
const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filePath));
|
|
397
401
|
|
|
402
|
+
if (!sourceFile) {
|
|
403
|
+
process.stderr.write("jsdoc-scribe: could not parse " + filePath + "\n");
|
|
404
|
+
return { filePath, moduleName: null, description: null, since: null, functions: [], classes: [], interfaces: [], typeAliases: [], enums: [], variables: [] };
|
|
405
|
+
}
|
|
398
406
|
const moduleDoc = extractModuleDoc(sourceFile);
|
|
399
407
|
const result = { filePath, moduleName: moduleDoc.moduleName, description: moduleDoc.description, since: moduleDoc.since, functions: [], classes: [], interfaces: [], typeAliases: [], enums: [], variables: [] };
|
|
400
408
|
|
|
401
409
|
function visit(node) {
|
|
410
|
+
try {
|
|
402
411
|
if (ts.isFunctionDeclaration(node) && node.body && node.name) {
|
|
403
412
|
result.functions.push(extractFunction(node, sourceFile));
|
|
404
413
|
} else if (ts.isClassDeclaration(node)) {
|
|
@@ -446,6 +455,9 @@ function extractModule(filePath) {
|
|
|
446
455
|
}
|
|
447
456
|
}
|
|
448
457
|
}
|
|
458
|
+
} catch (err) {
|
|
459
|
+
process.stderr.write("jsdoc-scribe: skipped node in " + filePath + " — " + err.message + "\n");
|
|
460
|
+
}
|
|
449
461
|
ts.forEachChild(node, visit);
|
|
450
462
|
}
|
|
451
463
|
|
package/lib/renderer.js
CHANGED
|
@@ -8,7 +8,7 @@ const path = require("path");
|
|
|
8
8
|
|
|
9
9
|
const THEMES = {
|
|
10
10
|
default: {
|
|
11
|
-
light: `:root{--bg:#f8f9fc;--surface:#fff;--border:#e0e4f0;--text:#1a1a2e;--text2:#
|
|
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
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
13
|
toggleBtn: true,
|
|
14
14
|
},
|
|
@@ -25,26 +25,37 @@ const THEMES = {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
|
-
// CSS
|
|
28
|
+
// CSS — layout, components, responsive, print
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
|
|
31
31
|
const CSS_STRUCTURE = `
|
|
32
|
+
/* == Reset & base == */
|
|
32
33
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
34
|
+
html{scroll-behavior:smooth}
|
|
33
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}
|
|
34
|
-
a{color:var(--accent);text-decoration:none}
|
|
36
|
+
a{color:var(--accent);text-decoration:none}
|
|
37
|
+
a:hover{text-decoration:underline}
|
|
35
38
|
code,pre{font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.
|
|
39
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
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}
|
|
42
50
|
.theme-btn:hover{background:var(--sidebar-active)}
|
|
43
|
-
|
|
44
|
-
|
|
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}
|
|
45
56
|
.search-box::placeholder{color:var(--sidebar-title)}
|
|
46
57
|
.search-box:focus{border-color:var(--accent)}
|
|
47
|
-
.search-results{display:none;position:absolute;left:
|
|
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)}
|
|
48
59
|
.search-results.visible{display:block}
|
|
49
60
|
.search-result-item{display:block;padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--search-border);text-decoration:none}
|
|
50
61
|
.search-result-item:hover{background:var(--sidebar-active)}
|
|
@@ -52,31 +63,65 @@ code,pre{font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace
|
|
|
52
63
|
.sr-kind{font-size:11px;color:var(--sidebar-title);margin-left:6px}
|
|
53
64
|
.sr-module{font-size:11px;color:var(--sidebar-title);display:block;margin-top:1px;opacity:.7}
|
|
54
65
|
.search-no-results{padding:10px 12px;color:var(--sidebar-title);font-size:13px}
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.sidebar-
|
|
59
|
-
.sidebar-
|
|
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}
|
|
60
75
|
.sidebar-dir-toggle::-webkit-details-marker{display:none}
|
|
61
|
-
.sidebar-dir-toggle::before{content:'▶';font-size:8px;transition:transform .15s}
|
|
76
|
+
.sidebar-dir-toggle::before{content:'▶';font-size:8px;transition:transform .15s;flex-shrink:0}
|
|
62
77
|
details[open] .sidebar-dir-toggle::before{transform:rotate(90deg)}
|
|
63
|
-
.sidebar-link-indent{padding-left:
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.
|
|
68
|
-
.
|
|
69
|
-
.
|
|
70
|
-
.
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
.
|
|
74
|
-
.
|
|
75
|
-
.
|
|
76
|
-
|
|
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}
|
|
77
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}
|
|
78
121
|
.copy-btn:hover{background:var(--accent2);border-color:var(--accent);color:var(--accent)}
|
|
79
122
|
.copy-btn.copied{background:#e8f5e9;border-color:#43a047;color:#2e7d32}
|
|
123
|
+
|
|
124
|
+
/* == Badges == */
|
|
80
125
|
.badge{display:inline-block;padding:2px 7px;border-radius:4px;font-size:11px;font-weight:600;margin-right:3px;margin-top:5px}
|
|
81
126
|
.badge-exported{background:#e8f5e9;color:#2e7d32}
|
|
82
127
|
.badge-async{background:#e3f2fd;color:#1565c0}
|
|
@@ -94,6 +139,8 @@ details[open] .sidebar-dir-toggle::before{transform:rotate(90deg)}
|
|
|
94
139
|
.badge-since{background:#e8f5e9;color:#2e7d32}
|
|
95
140
|
.deprecated-notice{background:var(--dep-bg);color:var(--dep-text);border-radius:5px;padding:6px 12px;font-size:12px;margin-top:8px}
|
|
96
141
|
.since-label{font-size:11px;color:var(--text3);margin-top:4px}
|
|
142
|
+
|
|
143
|
+
/* == Tables == */
|
|
97
144
|
.throws-table{width:100%;border-collapse:collapse;margin-top:8px;font-size:13px;border:1px solid var(--throws-bg)}
|
|
98
145
|
.throws-table th{text-align:left;padding:5px 10px;background:var(--throws-bg);color:var(--throws-text);font-weight:600;font-size:12px}
|
|
99
146
|
.throws-table td{padding:5px 10px;border-top:1px solid var(--border);color:var(--text);vertical-align:top}
|
|
@@ -104,6 +151,8 @@ details[open] .sidebar-dir-toggle::before{transform:rotate(90deg)}
|
|
|
104
151
|
.params-table td code{background:var(--code-bg);padding:1px 5px;border-radius:3px;font-size:12px}
|
|
105
152
|
.returns{margin-top:8px;font-size:13px;color:var(--text2)}
|
|
106
153
|
.returns code{background:var(--code-bg);padding:1px 6px;border-radius:3px;font-family:monospace}
|
|
154
|
+
|
|
155
|
+
/* == Collapsible sections == */
|
|
107
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}
|
|
108
157
|
.collapse-toggle::-webkit-details-marker{display:none}
|
|
109
158
|
.collapse-toggle::before{content:'▶';font-size:9px;display:inline-block;transition:transform .15s;color:var(--sub-label)}
|
|
@@ -112,30 +161,88 @@ details[open] .collapse-toggle::before{transform:rotate(90deg)}
|
|
|
112
161
|
.method-row{margin-top:8px;padding:10px 0;border-top:1px solid var(--method-border)}
|
|
113
162
|
.method-sig{font-family:monospace;font-size:13px;color:var(--text)}
|
|
114
163
|
.method-desc{font-size:12px;color:var(--text2);margin-top:4px}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.module-
|
|
118
|
-
.module-card
|
|
119
|
-
.module-card
|
|
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)}
|
|
120
171
|
.module-card-desc{font-size:12px;color:var(--text3);margin-top:6px;line-height:1.4}
|
|
172
|
+
|
|
173
|
+
/* == Breadcrumb == */
|
|
121
174
|
.breadcrumb{font-size:13px;color:var(--text3);margin-bottom:20px}
|
|
122
175
|
.breadcrumb a{color:var(--accent)}
|
|
176
|
+
|
|
177
|
+
/* == Misc == */
|
|
123
178
|
.anchor-link{color:var(--sidebar-title);opacity:0;font-size:13px;margin-left:6px;transition:opacity .15s}
|
|
124
179
|
.card:hover .anchor-link{opacity:.6}
|
|
125
180
|
.anchor-link:hover{opacity:1;text-decoration:none}
|
|
126
181
|
.empty{color:var(--text3);font-size:13px;font-style:italic}
|
|
127
182
|
.link-ref{color:var(--accent);text-decoration:none;font-size:inherit}
|
|
128
183
|
.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
184
|
.source-link{font-size:11px;color:var(--text3);font-family:monospace;text-decoration:none;margin-left:6px;opacity:.7}
|
|
133
185
|
.source-link:hover{opacity:1;text-decoration:underline;color:var(--accent)}
|
|
134
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
|
+
}
|
|
135
236
|
`;
|
|
136
237
|
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// App JS (written to assets/app.js)
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
137
242
|
const CLIENT_JS = `
|
|
138
243
|
(function(){
|
|
244
|
+
var _pfx=(window.location.pathname.replace(/\\/g,'/').indexOf('/modules/')!==-1)?'../':'';
|
|
245
|
+
// Theme
|
|
139
246
|
var THEME_KEY='jsdoc-scribe-theme';
|
|
140
247
|
var saved=localStorage.getItem(THEME_KEY);
|
|
141
248
|
if(saved) document.documentElement.setAttribute('data-theme',saved);
|
|
@@ -150,6 +257,7 @@ const CLIENT_JS = `
|
|
|
150
257
|
btn.textContent=next==='dark'?'Light':'Dark';
|
|
151
258
|
});
|
|
152
259
|
}
|
|
260
|
+
// Copy button
|
|
153
261
|
document.addEventListener('click',function(e){
|
|
154
262
|
var b=e.target.closest('.copy-btn');
|
|
155
263
|
if(!b) return;
|
|
@@ -158,33 +266,66 @@ const CLIENT_JS = `
|
|
|
158
266
|
setTimeout(function(){b.textContent='Copy';b.classList.remove('copied');},1500);
|
|
159
267
|
});
|
|
160
268
|
});
|
|
161
|
-
|
|
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};});
|
|
162
271
|
var box=document.getElementById('search-box');
|
|
163
272
|
var panel=document.getElementById('search-results');
|
|
164
|
-
if(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
273
|
+
if(box&&panel){
|
|
274
|
+
function esc(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');}
|
|
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
|
+
});
|
|
171
295
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
});
|
|
180
328
|
}
|
|
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
329
|
})();
|
|
189
330
|
`;
|
|
190
331
|
|
|
@@ -204,17 +345,44 @@ function metaHtml(item){
|
|
|
204
345
|
return out;
|
|
205
346
|
}
|
|
206
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
|
+
|
|
207
376
|
function descHtml(item,symbolMap,filePath){
|
|
208
377
|
var out='';
|
|
209
378
|
if(item.description) out+='<div class="card-desc">'+(symbolMap?resolveLinks(item.description,symbolMap,filePath):esc(item.description))+'</div>';
|
|
210
379
|
out+=metaHtml(item);
|
|
211
|
-
if(item.example) out+='<div class="card-example"><pre>'+
|
|
380
|
+
if(item.example) out+='<div class="card-example"><pre>'+highlightCode(item.example)+'</pre></div>';
|
|
212
381
|
return out;
|
|
213
382
|
}
|
|
214
383
|
|
|
215
384
|
function renderParams(params, jsdocParams){
|
|
216
385
|
if(!params||!params.length) return '';
|
|
217
|
-
// Build a lookup of JSDoc @param enrichments keyed by name
|
|
218
386
|
var jmap={};
|
|
219
387
|
(jsdocParams||[]).forEach(function(p){jmap[p.name]=p;});
|
|
220
388
|
var html='<table class="params-table"><thead><tr><th>Parameter</th><th>Type</th><th>Optional</th><th>Description</th></tr></thead><tbody>';
|
|
@@ -228,7 +396,7 @@ function renderParams(params, jsdocParams){
|
|
|
228
396
|
|
|
229
397
|
function renderReturns(returnType, returnsTag){
|
|
230
398
|
var type=returnsTag&&returnsTag.type&&returnsTag.type!=='any'?returnsTag.type:returnType;
|
|
231
|
-
var desc=returnsTag&&returnsTag.description?'
|
|
399
|
+
var desc=returnsTag&&returnsTag.description?'— '+esc(returnsTag.description):'';
|
|
232
400
|
return'<div class="returns">Returns: <code>'+esc(type)+'</code> '+desc+'</div>';
|
|
233
401
|
}
|
|
234
402
|
|
|
@@ -241,14 +409,12 @@ function renderThrows(throws){
|
|
|
241
409
|
|
|
242
410
|
function resolveLinks(text, symbolMap, filePath, moduleHtmlPathFn, modules){
|
|
243
411
|
if(!text||!symbolMap) return esc(text);
|
|
244
|
-
// Replace {@link Symbol} and {@link Symbol#method} with anchor tags
|
|
245
412
|
return esc(text).replace(/\{@link ([^}]+)\}/g, function(_, ref){
|
|
246
413
|
var parts=ref.trim().split('#');
|
|
247
414
|
var sym=parts[0].trim();
|
|
248
415
|
var method=parts[1]?parts[1].trim():null;
|
|
249
416
|
var entry=symbolMap[sym];
|
|
250
417
|
if(!entry) return'<code>'+esc(ref)+'</code>';
|
|
251
|
-
// Are we on the same module page?
|
|
252
418
|
var targetPath=entry.modulePath;
|
|
253
419
|
var href=targetPath===filePath?'':(targetPath||'');
|
|
254
420
|
if(method) href+='#meth-'+sym+'_'+method;
|
|
@@ -278,10 +444,10 @@ function buildCss(theme){
|
|
|
278
444
|
return t.light+(t.dark||'')+CSS_STRUCTURE;
|
|
279
445
|
}
|
|
280
446
|
|
|
281
|
-
function page(title,sidebarHtml,bodyHtml,
|
|
282
|
-
var
|
|
283
|
-
var
|
|
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<
|
|
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>';
|
|
285
451
|
}
|
|
286
452
|
|
|
287
453
|
// ---------------------------------------------------------------------------
|
|
@@ -341,10 +507,10 @@ function buildSearchIndex(modules,prefix){
|
|
|
341
507
|
}
|
|
342
508
|
|
|
343
509
|
// ---------------------------------------------------------------------------
|
|
344
|
-
// Sidebar
|
|
510
|
+
// Sidebar (with symbol tree under active module)
|
|
345
511
|
// ---------------------------------------------------------------------------
|
|
346
512
|
|
|
347
|
-
function buildSidebar(modules,projectName,version,activePath,rootPrefix,showToggle){
|
|
513
|
+
function buildSidebar(modules,projectName,version,activePath,rootPrefix,showToggle,activeModule){
|
|
348
514
|
var prefix=rootPrefix||'';
|
|
349
515
|
var html='<div class="sidebar-header">'
|
|
350
516
|
+'<div><a href="'+prefix+'index.html">'+esc(projectName)+'</a>'+(version?'<span class="version">v'+esc(version)+'</span>':'')+'</div>'
|
|
@@ -363,6 +529,26 @@ function buildSidebar(modules,projectName,version,activePath,rootPrefix,showTogg
|
|
|
363
529
|
});
|
|
364
530
|
var hasGroups=order.some(function(d){return d!=='';});
|
|
365
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
|
+
|
|
366
552
|
order.forEach(function(dir){
|
|
367
553
|
var mods=groups[dir];
|
|
368
554
|
if(hasGroups&&dir){
|
|
@@ -372,8 +558,9 @@ function buildSidebar(modules,projectName,version,activePath,rootPrefix,showTogg
|
|
|
372
558
|
var rel=moduleHtmlPath(mod.filePath,modules);
|
|
373
559
|
var label=moduleLabel(mod.filePath,modules);
|
|
374
560
|
var name=label.slice(dir.length+1);
|
|
375
|
-
var
|
|
376
|
-
html+='<a class="sidebar-link sidebar-link-indent'+active+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
|
|
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);
|
|
377
564
|
});
|
|
378
565
|
html+='</details>';
|
|
379
566
|
} else {
|
|
@@ -381,14 +568,42 @@ function buildSidebar(modules,projectName,version,activePath,rootPrefix,showTogg
|
|
|
381
568
|
var rel=moduleHtmlPath(mod.filePath,modules);
|
|
382
569
|
var label=moduleLabel(mod.filePath,modules);
|
|
383
570
|
var name=dir?label.slice(dir.length+1):label;
|
|
384
|
-
var
|
|
385
|
-
html+='<a class="sidebar-link'+active+'" href="'+esc(prefix+rel)+'">'+esc(name)+'</a>';
|
|
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);
|
|
386
574
|
});
|
|
387
575
|
}
|
|
388
576
|
});
|
|
389
577
|
return html+'</div>';
|
|
390
578
|
}
|
|
391
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
|
+
|
|
392
607
|
// ---------------------------------------------------------------------------
|
|
393
608
|
// Item renderers
|
|
394
609
|
// ---------------------------------------------------------------------------
|
|
@@ -398,7 +613,7 @@ function renderFunction(fn,filePath,sourceUrl,symbolMap){
|
|
|
398
613
|
var sig=fn.name+'('+paramStr+'): '+fn.returnType;
|
|
399
614
|
var id=anchorId('fn',fn.name);
|
|
400
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('');
|
|
401
|
-
return'<div class="card" id="'+id+'">'
|
|
616
|
+
return'<div class="card card-fn" id="'+id+'">'
|
|
402
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>'
|
|
403
618
|
+'<div>'+badges+'</div>'+descHtml(fn,symbolMap,filePath)
|
|
404
619
|
+renderParams(fn.params,fn.jsdocParams)
|
|
@@ -410,7 +625,7 @@ function renderFunction(fn,filePath,sourceUrl,symbolMap){
|
|
|
410
625
|
function renderClass(cls,filePath,sourceUrl,symbolMap){
|
|
411
626
|
var id=anchorId('cls',cls.name);
|
|
412
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('');
|
|
413
|
-
var inner='<div class="card" id="'+id+'">'
|
|
628
|
+
var inner='<div class="card card-cls" id="'+id+'">'
|
|
414
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>'
|
|
415
630
|
+copyBtn('class '+cls.name+(cls.extends.length?' extends '+cls.extends.join(', '):''))+'</div>'
|
|
416
631
|
+'<div>'+badges+'</div>'+descHtml(cls,symbolMap,filePath);
|
|
@@ -449,7 +664,7 @@ function renderClass(cls,filePath,sourceUrl,symbolMap){
|
|
|
449
664
|
|
|
450
665
|
function renderInterface(iface,filePath,sourceUrl,symbolMap){
|
|
451
666
|
var id=anchorId('iface',iface.name);
|
|
452
|
-
var html='<div class="card" id="'+id+'">'
|
|
667
|
+
var html='<div class="card card-iface" id="'+id+'">'
|
|
453
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>'
|
|
454
669
|
+(iface.isExported?badge('exported','exported'):'')+descHtml(iface,symbolMap,filePath);
|
|
455
670
|
if(iface.properties.length){
|
|
@@ -467,7 +682,7 @@ function renderInterface(iface,filePath,sourceUrl,symbolMap){
|
|
|
467
682
|
|
|
468
683
|
function renderEnum(enm,filePath,sourceUrl,symbolMap){
|
|
469
684
|
var id=anchorId('enum',enm.name);
|
|
470
|
-
var html='<div class="card" id="'+id+'">'
|
|
685
|
+
var html='<div class="card card-enum" id="'+id+'">'
|
|
471
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>'
|
|
472
687
|
+(enm.isExported?badge('exported','exported'):'')+descHtml(enm,symbolMap,filePath)
|
|
473
688
|
+'<table class="params-table" style="margin-top:10px"><thead><tr><th>Member</th><th>Value</th></tr></thead><tbody>';
|
|
@@ -477,7 +692,7 @@ function renderEnum(enm,filePath,sourceUrl,symbolMap){
|
|
|
477
692
|
|
|
478
693
|
function renderTypeAlias(ta,filePath,sourceUrl,symbolMap){
|
|
479
694
|
var id=anchorId('type',ta.name);
|
|
480
|
-
return'<div class="card" id="'+id+'">'
|
|
695
|
+
return'<div class="card card-type" id="'+id+'">'
|
|
481
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>'
|
|
482
697
|
+(ta.isExported?badge('exported','exported'):'')+descHtml(ta,symbolMap,filePath)+'</div>';
|
|
483
698
|
}
|
|
@@ -485,91 +700,132 @@ function renderTypeAlias(ta,filePath,sourceUrl,symbolMap){
|
|
|
485
700
|
function renderVariable(v,filePath,sourceUrl,symbolMap){
|
|
486
701
|
var id=anchorId('var',v.name);
|
|
487
702
|
var decl=(v.isConst?'const':'let')+' '+v.name+': '+v.type;
|
|
488
|
-
return'<div class="card" id="'+id+'">'
|
|
703
|
+
return'<div class="card card-var" id="'+id+'">'
|
|
489
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>'
|
|
490
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>';
|
|
491
706
|
}
|
|
492
707
|
|
|
493
708
|
function section(title,items,renderFn,filePath,sourceUrl,symbolMap){
|
|
494
709
|
if(!items||!items.length) return '';
|
|
495
|
-
|
|
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>';
|
|
496
712
|
}
|
|
497
713
|
|
|
498
714
|
// ---------------------------------------------------------------------------
|
|
499
|
-
// Site builder
|
|
715
|
+
// Site builder — helpers
|
|
500
716
|
// ---------------------------------------------------------------------------
|
|
501
717
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
var
|
|
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={};
|
|
718
|
+
/** Build the symbol map for @link cross-reference resolution. */
|
|
719
|
+
function buildSymbolMap(modules){
|
|
720
|
+
var map={};
|
|
510
721
|
modules.forEach(function(mod){
|
|
511
722
|
var rel=moduleHtmlPath(mod.filePath,modules);
|
|
512
|
-
function reg(name,aid){
|
|
723
|
+
function reg(name,aid){map[name]={anchorId:aid,modulePath:rel};}
|
|
513
724
|
mod.functions.forEach(function(f){reg(f.name,anchorId('fn',f.name));});
|
|
514
|
-
mod.classes.forEach(function(c){
|
|
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
|
+
});
|
|
515
729
|
mod.interfaces.forEach(function(i){reg(i.name,anchorId('iface',i.name));});
|
|
516
730
|
mod.typeAliases.forEach(function(t){reg(t.name,anchorId('type',t.name));});
|
|
517
731
|
mod.enums.forEach(function(e){reg(e.name,anchorId('enum',e.name));});
|
|
518
732
|
mod.variables.forEach(function(v){reg(v.name,anchorId('var',v.name));});
|
|
519
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);
|
|
520
792
|
var showToggle=(THEMES[theme]||THEMES.default).toggleBtn;
|
|
521
793
|
var pages=[];
|
|
522
|
-
var idxIdx=buildSearchIndex(modules,'');
|
|
523
|
-
var modIdx=buildSearchIndex(modules,'../');
|
|
524
794
|
|
|
525
|
-
//
|
|
526
|
-
|
|
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
|
|
527
801
|
var totalFns=modules.reduce(function(s,m){return s+m.functions.length;},0);
|
|
528
802
|
var totalCls=modules.reduce(function(s,m){return s+m.classes.length;},0);
|
|
529
|
-
var
|
|
530
|
-
+'<div class="page-subtitle">'+modules.length+' module(s) · '+totalFns+' function(s) · '+totalCls+' class(es)</div>'
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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>';
|
|
803
|
+
var idxHeader='<div class="page-title">'+esc(projectName)+'</div>'
|
|
804
|
+
+'<div class="page-subtitle">'+modules.length+' module(s) · '+totalFns+' function(s) · '+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),
|
|
545
808
|
});
|
|
546
|
-
body+='</div></div>';
|
|
547
|
-
pages.push({path:'index.html',html:page(projectName,sidebar,body,idxIdx,theme)});
|
|
548
809
|
|
|
549
|
-
//
|
|
810
|
+
// Per-module pages
|
|
550
811
|
modules.forEach(function(mod){
|
|
551
812
|
var rel=moduleHtmlPath(mod.filePath,modules);
|
|
552
813
|
var label=moduleLabel(mod.filePath,modules);
|
|
553
|
-
var
|
|
554
|
-
var mbody='<div class="breadcrumb"><a href="../index.html">'+esc(projectName)+'</a> / '+esc(label)+'</div>'
|
|
814
|
+
var modHeader='<div class="breadcrumb"><a href="../index.html">'+esc(projectName)+'</a> / '+esc(label)+'</div>'
|
|
555
815
|
+'<div class="page-title">'+esc(label)+'</div>'
|
|
556
|
-
+'<div class="page-subtitle"
|
|
816
|
+
+'<div class="page-subtitle">'+esc(mod.filePath)+'</div>'
|
|
557
817
|
+(mod.description?'<p class="module-desc">'+esc(mod.description)+'</p>':'');
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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)});
|
|
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
|
+
});
|
|
570
827
|
});
|
|
571
828
|
|
|
572
829
|
return pages;
|
|
573
830
|
}
|
|
574
|
-
|
|
575
831
|
module.exports = { buildSite, moduleLabel, moduleHtmlPath };
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2
|
+
"name": "jsdoc-scribe",
|
|
3
|
+
"version": "1.11.0",
|
|
4
|
+
"description": "Pure, deterministic, AST-based JSDoc comment generator and documentation site builder for JavaScript and TypeScript. No AI involved.",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./lib/index.js",
|
|
8
|
+
"./docs": "./lib/docs.js"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"gen-comments": "./bin/cli.js",
|
|
12
|
+
"gen-docs": "./bin/gen-docs.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"bin",
|
|
16
|
+
"lib",
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"demo": "node bin/cli.js sample",
|
|
23
|
+
"docs": "node bin/gen-docs.js sample --out docs --title jsdoc-scribe",
|
|
24
|
+
"test": "node test/run.js",
|
|
25
|
+
"prepublishOnly": "npm test"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"jsdoc",
|
|
29
|
+
"comments",
|
|
30
|
+
"documentation",
|
|
31
|
+
"typescript",
|
|
32
|
+
"javascript",
|
|
33
|
+
"cli",
|
|
34
|
+
"ast",
|
|
35
|
+
"code-documentation",
|
|
36
|
+
"comment-generator",
|
|
37
|
+
"autodoc",
|
|
38
|
+
"docs-generator"
|
|
39
|
+
],
|
|
40
|
+
"author": {
|
|
41
|
+
"name": "Chintan Goswami"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=14"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/imchintoo/jsdoc-scribe.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/imchintoo/jsdoc-scribe#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/imchintoo/jsdoc-scribe/issues"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"typescript": ">=5.0.0"
|
|
57
|
+
}
|
|
58
58
|
}
|