Browse Source

doc: replace oxide theme with custom one

pull/1200/head
Vitaly Puzrin 2 months ago
parent
commit
1c197347ef
  1. 375
      package-lock.json
  2. 1
      package.json
  3. 63
      support/typedoc-custom.css
  4. 115
      support/typedoc-oxide-fixes.mjs
  5. 262
      support/typedoc_condensed_theme/condensed_theme.css
  6. 245
      support/typedoc_condensed_theme/condensed_theme_plugin.mjs
  7. 10
      typedoc.config.mjs

375
package-lock.json

@ -49,7 +49,6 @@
"tinybench": "^6.0.2",
"typedoc": "^0.28.19",
"typedoc-plugin-missing-exports": "^4.1.4",
"typedoc-theme-oxide": "^0.3.0",
"typescript": "^6.0.3",
"vite": "^8.1.5",
"vite-plugin-singlefile": "^2.3.3"
@ -1771,13 +1770,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"dev": true,
"license": "ISC"
},
"node_modules/brace-expansion": {
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
@ -1913,50 +1905,6 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/cheerio": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz",
"integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==",
"dev": true,
"license": "MIT",
"dependencies": {
"cheerio-select": "^2.1.0",
"dom-serializer": "^2.0.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"encoding-sniffer": "^0.2.1",
"htmlparser2": "^10.1.0",
"parse5": "^7.3.0",
"parse5-htmlparser2-tree-adapter": "^7.1.0",
"parse5-parser-stream": "^7.1.2",
"undici": "^7.19.0",
"whatwg-mimetype": "^4.0.0"
},
"engines": {
"node": ">=20.18.1"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-select": "^5.1.0",
"css-what": "^6.1.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cliui": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
@ -2056,36 +2004,6 @@
"node": ">= 8"
}
},
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.1.0",
"domhandler": "^5.0.2",
"domutils": "^3.0.1",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-what": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/data-view-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
@ -2237,78 +2155,6 @@
"node": ">=0.10.0"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"dev": true,
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/dom-serializer/node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "BSD-2-Clause"
},
"node_modules/domhandler": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.3.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^2.0.0",
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dts-resolver": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-3.0.0.tgz",
@ -2359,20 +2205,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/encoding-sniffer": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
"integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
"dev": true,
"license": "MIT",
"dependencies": {
"iconv-lite": "^0.6.3",
"whatwg-encoding": "^3.1.1"
},
"funding": {
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
}
},
"node_modules/enhanced-resolve": {
"version": "5.24.5",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz",
@ -3102,35 +2934,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/flexsearch": {
"version": "0.8.212",
"resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.8.212.tgz",
"integrity": "sha512-wSyJr1GUWoOOIISRu+X2IXiOcVfg9qqBRyCPRUdLMIGJqPzMo+jMRlvE83t14v1j0dRMEaBbER/adQjp6Du2pw==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/ts-thomas"
},
{
"type": "paypal",
"url": "https://www.paypal.com/donate/?hosted_button_id=GEVR88FC9BWRW"
},
{
"type": "opencollective",
"url": "https://opencollective.com/flexsearch"
},
{
"type": "patreon",
"url": "https://patreon.com/user?u=96245532"
},
{
"type": "liberapay",
"url": "https://liberapay.com/ts-thomas"
}
],
"license": "Apache-2.0"
},
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
@ -3623,52 +3426,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/htmlparser2": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz",
"integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==",
"dev": true,
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"license": "MIT",
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.3",
"domutils": "^3.2.2",
"entities": "^7.0.1"
}
},
"node_modules/htmlparser2/node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -5089,19 +4846,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -5316,59 +5060,6 @@
"node": ">=6"
}
},
"node_modules/parse5": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
"dev": true,
"license": "MIT",
"dependencies": {
"entities": "^6.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"domhandler": "^5.0.3",
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5-parser-stream": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
"dev": true,
"license": "MIT",
"dependencies": {
"parse5": "^7.0.0"
},
"funding": {
"url": "https://github.com/inikulin/parse5?sponsor=1"
}
},
"node_modules/parse5/node_modules/entities": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -5901,13 +5592,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT"
},
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@ -6089,16 +5773,6 @@
"node": ">=8"
}
},
"node_modules/slug": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/slug/-/slug-11.0.1.tgz",
"integrity": "sha512-VrM060OM/E7rdLQSnp6JHrzFfJFmqQBp0+TMhZStnEB8PfNliaZ9UWYjTHGHLUFVJorZ8TjVd/aKvIxHWU2O7g==",
"dev": true,
"license": "MIT",
"bin": {
"slug": "cli.js"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -6661,21 +6335,6 @@
"typedoc": "^0.28.1"
}
},
"node_modules/typedoc-theme-oxide": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/typedoc-theme-oxide/-/typedoc-theme-oxide-0.3.0.tgz",
"integrity": "sha512-K16JtPvyF4DJS2wH5xgumnUDlgEW5bzB4uBSxBM18gBvFCsTdYD713vSzTXvZdPe19a3HEnjXc0wZ1cYqshGeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"cheerio": "^1.1.2",
"flexsearch": "^0.8.212",
"slug": "^11.0.1"
},
"peerDependencies": {
"typedoc": "^0.28.4"
}
},
"node_modules/typedoc/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
@ -6778,16 +6437,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/undici": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
"integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/unicorn-magic": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
@ -6949,30 +6598,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/whatwg-encoding": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
"deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation",
"dev": true,
"license": "MIT",
"dependencies": {
"iconv-lite": "0.6.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/whatwg-mimetype": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

1
package.json

@ -104,7 +104,6 @@
"tinybench": "^6.0.2",
"typedoc": "^0.28.19",
"typedoc-plugin-missing-exports": "^4.1.4",
"typedoc-theme-oxide": "^0.3.0",
"typescript": "^6.0.3",
"vite": "^8.1.5",
"vite-plugin-singlefile": "^2.3.3"

63
support/typedoc-custom.css

@ -1,63 +0,0 @@
/* Missed alert styles */
:root {
--color-alert-note: #0969da;
--color-alert-tip: #1a7f37;
--color-alert-important: #8250df;
--color-alert-warning: #9a6700;
--color-alert-caution: #cf222e;
}
:root[data-theme="dark"],
:root[data-theme="ayu"] {
--color-alert-note: #4493f8;
--color-alert-tip: #3fb950;
--color-alert-important: #ab7df8;
--color-alert-warning: #d29922;
--color-alert-caution: #f85149;
}
.tsd-alert {
padding: 8px 16px;
margin-bottom: 16px;
border-left: 0.25em solid var(--alert-color);
}
.tsd-alert > :last-child {
margin-bottom: 0;
}
.tsd-alert-title {
color: var(--alert-color);
display: inline-flex;
align-items: center;
font-weight: 500;
}
.tsd-alert-title span {
margin-left: 4px;
}
.tsd-alert-note { --alert-color: var(--color-alert-note); }
.tsd-alert-tip { --alert-color: var(--color-alert-tip); }
.tsd-alert-important { --alert-color: var(--color-alert-important); }
.tsd-alert-warning { --alert-color: var(--color-alert-warning); }
.tsd-alert-caution { --alert-color: var(--color-alert-caution); }
/* Fix `@example` tags display */
.item-table.comment-tags {
display: block;
}
.item-table.comment-tags > dt {
margin: 1em 0 0.5em;
}
.item-table.comment-tags > dd {
margin: 0;
}
.item-table.comment-tags > dd > pre {
margin-top: 0;
}

115
support/typedoc-oxide-fixes.mjs

@ -1,115 +0,0 @@
import * as fs from 'fs/promises'
import * as path from 'path'
import { deflateSync as deflate, inflateSync as inflate } from 'zlib'
import { RendererEvent, PageEvent, ReflectionKind } from 'typedoc'
import { itemSlug } from 'typedoc-theme-oxide/dist/plugin/context/utils.js'
// Workarounds for https://github.com/balthild/typedoc-theme-oxide - drop them
// once the theme fixes the issues upstream.
export function load (app) {
app.renderer.on(RendererEvent.BEGIN, (event) => {
for (const refl of Object.values(event.project.reflections)) {
// The theme renders `categories` and `groups` side by side, ignoring
// `navigation.includeCategories`, so everything shows up twice. Drop the
// groups wherever categories exist; members without categories (class
// properties, methods, ...) keep their groups.
if (refl.categories?.length && refl.groups?.length) refl.groups = undefined
// The theme appends default values right after the type with no
// separator ("boolean" + "false"). Carry the separator in the value.
if (refl.defaultValue && !refl.kindOf(ReflectionKind.SomeType)) {
refl.defaultValue = ' = ' + refl.defaultValue
}
}
})
// The theme builds its own "Source" and navigation links, ignoring
// `sourceLinkExternal`. Markdown links are already handled by TypeDoc.
const baseUrl = app.options.getValue('hostedBaseUrl')
app.renderer.on(PageEvent.END, (page) => {
page.contents = page.contents.replace(/<a\s[^>]*>/g, tag => {
if (/\btarget=/.test(tag)) return tag
const href = tag.match(/\shref="(https?:\/\/[^"]*)"/)?.[1]
if (!href || (href + '/').startsWith(baseUrl)) return tag
return tag.replace(/^<a\s/, '<a target="_blank" ')
})
})
// The theme emits its own member anchors ("property.core") but leaves the
// router on TypeDoc's scheme ("core"), so every {@link} into a member lands
// nowhere. Teach the router the anchors the theme actually prints.
const routerName = app.options.getValue('router')
const OxideRouter = app.renderer.routers.get(routerName)
app.renderer.routers.set(routerName, class extends OxideRouter {
createAnchor (target) { return itemSlug(target) }
})
const OxideTheme = app.renderer.themes.get('oxide')
class PatchedOxideTheme extends OxideTheme {
getRenderContext (page) {
const context = super.getRenderContext(page)
const itemLink = context.itemLink.bind(context)
const navigation = context.navigation.bind(context)
// The theme links functions, variables, type aliases and documents to an
// anchor on the parent page, but only classes, interfaces and namespaces
// ever get one. Link to the own page whenever the router made one.
context.itemLink = (item, forceNested) => (
this.router.hasOwnDocument(item) ? context.urlTo(item) : itemLink(item, forceNested)
)
context.navigation = (...args) => {
const fragment = navigation(...args)
for (const child of fragment.children) {
// Drop the hardcoded "Exports" link at the top of the sidebar - it is
// the only bare <ul> there, everything else is wrapped in <section>.
child.children = child.children.filter(x => x?.tag !== 'ul')
// The theme lists the sibling modules of the parent, rustdoc style.
// On a namespace page that is useful navigation, anywhere else it
// just glues the project namespaces on top. Keep the ".." link only.
if (args[0]?.model?.kindOf(ReflectionKind.SomeModule)) continue
for (const section of child.children.filter(x => x?.tag === 'section')) {
for (const list of section.children) {
list.children = list.children
.flat(Infinity)
.filter(x => x?.props?.class === 'parent-module')
}
}
}
return fragment
}
return context
}
// The search index is built with the same broken links as the sidebar, but
// by a private method. Repoint its entries once it has been written.
async postRender (event) {
await super.postRender(event)
const file = path.join(event.outputDirectory, 'assets', 'oxide', 'search-index.deflate')
const parts = JSON.parse(inflate(await fs.readFile(file)).toString())
for (const part of Object.values(parts)) {
if (!Array.isArray(part)) continue
for (const entry of part) {
const doc = Array.isArray(entry) ? entry[1] : null
if (typeof doc?.url !== 'string') continue
const refl = event.project.getReflectionById(doc.id)
if (refl && this.router.hasOwnDocument(refl)) doc.url = this.router.getFullUrl(refl)
}
}
await fs.writeFile(file, deflate(Buffer.from(JSON.stringify(parts))))
}
}
app.renderer.themes.set('oxide', PatchedOxideTheme)
}

262
support/typedoc_condensed_theme/condensed_theme.css

@ -0,0 +1,262 @@
/*
* Keep TypeDoc's default layout and navigation. This file only adapts the
* content typography and removes duplicated declarations.
*/
:root {
--cndnsd-font-serif: Charter, "Bitstream Charter", "Sitka Text", Cambria, serif;
--cndnsd-font-sans: Seravek, "Gill Sans Nova", Ubuntu, Calibri, "DejaVu Sans", source-sans-pro, sans-serif;
--cndnsd-font-mono: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
--cndnsd-target-animation: 0.65s cubic-bezier(0, 0, 0.1, 1) 0.1s cndnsd-target-fadein;
--cndnsd-target-color-faded: rgb(from var(--color-background-warning) r g b / 30%);
}
/* =============================================================================
* Base typography tweaks
* ========================================================================== */
h1, h2, h3, h4, h5, h6 {
font-family: var(--cndnsd-font-sans);
font-weight: 500;
}
h1 { font-size: 1.5rem; }
h2 { font-size: 1.375rem; }
h3 { font-size: 1.25rem; }
h4,
h5,
h6 {
font-size: 1rem;
}
code {
font-family: var(--cndnsd-font-mono);
background: oklch(
from var(--color-background-secondary)
calc(l - 0.025) c h
);
border-radius: 3px;
}
pre {
font-family: var(--cndnsd-font-mono);
margin: 0;
padding: .875rem;
background: var(--color-background-secondary);
border: 0;
border-radius: 6px;
font-size: 1rem;
line-height: 1.5;
}
.col-content .tsd-typography pre code {
padding: 0;
background: none;
}
/* Change default font for content only, don't touch side columns */
.col-content {
font: 16px/1.5 var(--cndnsd-font-serif);
}
/* =============================================================================
* Content
* ========================================================================== */
.tsd-breadcrumb {
font-family: var(--cndnsd-font-sans);
}
/* underline collapsible headers*/
.col-content .tsd-accordion-summary {
padding-bottom: .5rem;
border-bottom: 1px solid var(--color-accent);
}
/* Fix "open" chevron offset after typography changes. TBH, that's an icon bug */
.tsd-accordion[open] > .tsd-accordion-summary > svg:first-child {
translate: 0 4px;
}
.tsd-panel {
margin-bottom: 1.5rem;
}
/*
* Anchor links
*/
.tsd-anchor-icon {
opacity: 0;
position: absolute;
top: 0;
right: 100%;
margin: 0;
font-family: var(--cndnsd-font-sans);
font-style: normal;
font-weight: 400;
line-height: inherit;
text-decoration: none;
}
.tsd-anchor-link {
position: relative;
}
/* Remove permalinks in useless places in stead of styling
* - modules page
* - deprecated blocks
* - default values
*/
.tsd-member-group:has(> .tsd-member-summaries) .tsd-anchor-icon,
.tsd-tag-deprecated .tsd-anchor-icon,
.tsd-tag-defaultValue .tsd-anchor-icon {
display: none;
}
/* mouse over */
.tsd-anchor-icon:hover,
.tsd-anchor-icon:focus-visible,
.tsd-anchor-link:hover .tsd-anchor-icon {
opacity: 1;
}
/* fix permalink offset in markdown documents */
.tsd-panel > h1,
.tsd-panel > h2,
.tsd-panel > h3 {
margin-inline: 0;
padding-inline: 0;
}
/*
* Animate permalinks on page load
*/
@keyframes cndnsd-target-fadein {
from {
background-color: var(--color-background);
}
50% {
background-color: var(--color-background-warning);
}
to {
background-color: var(--cndnsd-target-color-faded);
}
}
/* various permalinks for generated code docs */
.tsd-member:target .tsd-signature,
.tsd-tag-example .tsd-anchor-link:target,
.tsd-accordion-summary:has(.tsd-anchor-link:target),
/* for markdown headers */
.tsd-panel.tsd-typography > .tsd-anchor-link:target {
background-color: var(--cndnsd-target-color-faded);
animation: var(--cndnsd-target-animation);
}
@media (prefers-reduced-motion) {
.col-content {
--cndnsd-target-animation: none;
}
}
/* Header-like signatures (properties, methods) */
.tsd-member .tsd-signature {
font-family: var(--cndnsd-font-mono);
padding: 0;
border: 0;
background: none;
font-size: 1rem;
line-height: 1.5;
overflow: visible;
white-space: pre-wrap;
}
.cndnsd-call-signature .tsd-signature {
font-weight: 600;
}
.cndnsd-call-signature .tsd-signature * {
font-weight: inherit;
}
/* offset signature descriptions */
.tsd-member > :not(.tsd-signature, .tsd-signatures, .tsd-anchor-icon),
.cndnsd-call-signature > :not(.tsd-signature) {
margin-left: 1.5rem;
}
/* Page top signatures */
.col-content > .tsd-panel :where(.tsd-signature),
.col-content > .tsd-signature {
font-family: var(--cndnsd-font-mono);
margin: 1rem 0;
padding: .875rem;
background: var(--color-background-secondary);
border: 0;
border-radius: 6px;
font-size: 1rem;
line-height: 1.5;
overflow-x: auto;
overflow-wrap: normal;
white-space: pre;
}
/*
* Source links
*/
.tsd-sources {
font: 1rem/1.5 var(--cndnsd-font-sans);
margin: 0;
white-space: normal; /* rollback for top */
}
.tsd-sources a {
color: var(--color-link);
text-decoration: none;
}
.tsd-member .tsd-signature {
display: flow-root;
}
/* Only the link moved up; implements/inherited notes share the same class. */
.tsd-member .tsd-signature .tsd-sources {
float: right;
margin: 0 0 0 .75rem;
}
.tsd-member .tsd-signature .tsd-sources ul {
margin: 0;
}
/* Restyle "default values" as one-line blocks of declaration. */
.tsd-tag-defaultValue {
display: flex;
align-items: baseline;
column-gap: 1rem;
font-size: 0.875rem;
font-weight: 400;
margin: 1rem 0;
}
.tsd-tag-defaultValue * {
font-size: inherit;
font-weight: inherit;
margin: 0;
}
/*
* Remove useless blocks
*/
/* These ones duplicate info and just add noise to the page. */
.tsd-index-group,
.tsd-type-declaration,
.tsd-returns-title,
.tsd-parameters-title {
display: none;
}
/* Hide "Type Parameters" blocks without valuable info. */
.tsd-panel:has(.tsd-type-parameter-list):not(:has(.tsd-comment)),
/* Hide call signature "Parameters" blocks without valuable info. */
.tsd-parameters:not(:has(.tsd-comment)) {
display: none;
}

245
support/typedoc_condensed_theme/condensed_theme_plugin.mjs

@ -0,0 +1,245 @@
/*
* Theme on top of the default one. Page layout and navigation are kept intact,
* only member presentation is changed:
*
* - source link is compact (`Source`) and moved into the signature line;
* - member `h3` heading is dropped, its anchor moves to the `section`;
* - permalink is a plain `§` instead of an icon;
* - own markup for signature and getter/setter lists;
* - page top keeps the default layout.
*
* The only place coupled to TypeDoc's markup is `findMemberSignature`.
*/
import { cpSync } from 'node:fs'
import { join } from 'node:path'
import {
DefaultTheme,
DefaultThemeRenderContext,
i18n,
JSX,
ReflectionKind,
RendererEvent
} from 'typedoc'
const h = JSX.createElement
/* =============================================================================
* Source links and permalinks
* ========================================================================== */
function sourceLink (context, source) {
if (!source.url) return 'Source'
const attributes = { href: source.url }
if (context.options.getValue('sourceLinkExternal')) {
attributes.target = '_blank'
}
return h('a', attributes, 'Source')
}
function sourceLinks (context, item) {
if (!item.sources?.length) return null
return h('aside', { class: 'tsd-sources' },
h('ul', null,
item.sources.map(source => h('li', null, sourceLink(context, source)))
)
)
}
// Hide sources for the duration of the default render: TypeDoc skips its own
// source links, but keeps implements/inherited/overrides notes untouched.
function renderWithoutSources (item, render) {
const saved = item.sources
item.sources = undefined
try {
return render()
} finally {
item.sources = saved
}
}
function anchorIcon (context, anchor) {
if (!anchor) return h(JSX.Fragment, null)
return h('a', {
href: `#${anchor}`,
'aria-label': i18n.theme_permalink(),
class: 'tsd-anchor-icon'
}, context.icons.anchor())
}
function addSignaturePermalink (context, signature, anchor) {
if (!anchor) return
signature.props = {
...signature.props,
class: `${signature.props.class} tsd-anchor-link`
}
signature.children.push(anchorIcon(context, anchor))
}
// TypeDoc renders the page top bypassing `member`, keep the default look there.
function isPageTop (context, reflection) {
return reflection === context.page.model
}
// Coupled to TypeDoc's markup: the signature line of `memberDeclaration` output.
function findMemberSignature (element) {
return element?.children.find(child =>
child?.props?.class?.split(' ').includes('tsd-signature')
)
}
/* =============================================================================
* Signature lists
* ========================================================================== */
function signatureItem (context, signature, permalinkAnchor, itemClass) {
const anchor = context.getAnchor(signature)
const className = [
context.getReflectionClasses(signature),
itemClass
].filter(Boolean).join(' ')
const signatureElement = h('div', {
class: 'tsd-signature',
id: anchor
},
sourceLinks(context, signature),
context.memberSignatureTitle(signature)
)
addSignaturePermalink(context, signatureElement, permalinkAnchor)
return h('li', className ? { class: className } : null, signatureElement,
h('div', { class: 'tsd-description' },
renderWithoutSources(signature, () => context.memberSignatureBody(signature))
)
)
}
function signatureList (context, declaration, items) {
const classes = context.getReflectionClasses(declaration)
return h('ul', {
class: classes ? `tsd-signatures ${classes}` : 'tsd-signatures'
}, items)
}
/* =============================================================================
* Render context
* ========================================================================== */
class CondensedThemeRenderContext extends DefaultThemeRenderContext {
constructor (...args) {
super(...args)
// In TypeDoc these renderers are fields, not prototype methods, so `super`
// can't reach them: overrides that augment the output capture the original
// here, while a full replacement is declared as a class field.
const renderMember = this.member
const renderMemberDeclaration = this.memberDeclaration
const renderMemberSignatures = this.memberSignatures
const renderMemberSources = this.memberSources
this.memberSources = item => h(JSX.Fragment, null,
sourceLinks(this, item),
renderWithoutSources(item, () => renderMemberSources(item))
)
this.memberDeclaration = declaration => {
if (isPageTop(this, declaration)) return renderMemberDeclaration(declaration)
const element = renderWithoutSources(declaration, () => renderMemberDeclaration(declaration))
const signature = findMemberSignature(element)
if (!signature) return element
const sources = sourceLinks(this, declaration)
if (sources) signature.children.unshift(sources)
addSignaturePermalink(this, signature, this.getAnchor(declaration))
return element
}
this.memberSignatures = declaration => {
if (isPageTop(this, declaration)) return renderMemberSignatures(declaration)
return signatureList(this, declaration, declaration.signatures?.map((signature, index) =>
signatureItem(
this,
signature,
index === 0 && this.getAnchor(declaration),
signature.kind === ReflectionKind.CallSignature && 'cndnsd-call-signature'
)
))
}
this.member = item => {
const element = renderMember(item)
const anchor = this.getAnchor(item)
element.children = element.children.filter(child => child?.tag !== 'h3')
element.props = { ...element.props, id: anchor }
return element
}
}
get icons () {
return {
...super.icons,
anchor: () => h('span', {
'aria-hidden': 'true',
class: 'cndnsd-anchor-symbol'
}, '§')
}
}
memberGetterSetter = declaration => signatureList(this, declaration, [
declaration.getSignature && signatureItem(
this,
declaration.getSignature,
this.getAnchor(declaration)
),
declaration.setSignature && signatureItem(
this,
declaration.setSignature,
!declaration.getSignature && this.getAnchor(declaration)
)
])
}
/* =============================================================================
* Theme and plugin
* ========================================================================== */
class CondensedTheme extends DefaultTheme {
ContextClass = CondensedThemeRenderContext
constructor (renderer) {
super(renderer)
this.owner.on(RendererEvent.END, event => {
cpSync(
join(import.meta.dirname, 'condensed_theme.css'),
join(event.outputDirectory, 'assets', 'condensed_theme.css')
)
})
}
}
export default function condensedThemePlugin (app) {
app.renderer.hooks.on('head.end', context => h('link', {
rel: 'stylesheet',
href: context.relativeURL('assets/condensed_theme.css')
}))
app.renderer.defineTheme('condensed', CondensedTheme)
}

10
typedoc.config.mjs

@ -1,3 +1,5 @@
import condensedThemePlugin from './support/typedoc_condensed_theme/condensed_theme_plugin.mjs'
export default {
entryPoints: ['src/index.ts'],
@ -15,13 +17,11 @@ export default {
plugin: [
'typedoc-plugin-missing-exports',
'typedoc-theme-oxide',
'./support/typedoc-oxide-fixes.mjs'
condensedThemePlugin
],
theme: 'oxide',
customCss: './support/typedoc-custom.css',
theme: 'condensed',
excludeExternals: true,
excludeInternal: true,
placeInternalsInOwningModule: true,
out: 'apidoc',

Loading…
Cancel
Save