From 737243ba8731e7b369286986e63f2f8698d58212 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Fri, 15 May 2026 18:42:42 +0200 Subject: [PATCH 01/27] A minimal setup for PDF rendering. --- .gitignore | 1 + docs/.gitignore | 6 +- docs/_config-pdf.yml | 25 + docs/_layouts/book.html | 13 + docs/package-lock.json | 3211 +++++++++++++++++++++++++++++++++++++++ docs/package.json | 9 + 6 files changed, 3263 insertions(+), 2 deletions(-) create mode 100644 docs/_config-pdf.yml create mode 100644 docs/_layouts/book.html create mode 100644 docs/package-lock.json create mode 100644 docs/package.json diff --git a/.gitignore b/.gitignore index 118e8e05..47e5d943 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .claude/ CLAUDE.md /.htmltest.yml +node_modules/ diff --git a/docs/.gitignore b/docs/.gitignore index 2a823267..94be70f1 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,5 +1,7 @@ -_site -_site-offline +_site/ +_site-offline/ +_site-pdf/ +_pdf/ .sass-cache .jekyll-cache .jekyll-metadata diff --git a/docs/_config-pdf.yml b/docs/_config-pdf.yml new file mode 100644 index 00000000..fbc0146f --- /dev/null +++ b/docs/_config-pdf.yml @@ -0,0 +1,25 @@ +# Layered on top of _config.yml when building the PDF source: +# bundle exec jekyll build --config _config.yml,_config-pdf.yml +# +# Produces _site-pdf/ containing every page rendered through _layouts/book.html +# (minimal HTML, no sidebar / nav / footer chrome). The output is the source +# tree that the concatenation step + pagedjs-cli operate on. + +destination: _site-pdf + +# Jekyll replaces top-level array keys when configs are layered, so the entire +# defaults: list has to be restated here, not just the layout entry we want to +# change. +defaults: + - scope: + path: "" + values: + layout: book + - scope: + path: "*/Images" + values: + image: true + - scope: + path: "assets/css" + values: + render_with_liquid: true diff --git a/docs/_layouts/book.html b/docs/_layouts/book.html new file mode 100644 index 00000000..c6aed015 --- /dev/null +++ b/docs/_layouts/book.html @@ -0,0 +1,13 @@ + + + + + {{ page.title }} β€” {{ site.title }} + + + +
+ {{ content }} +
+ + diff --git a/docs/package-lock.json b/docs/package-lock.json new file mode 100644 index 00000000..6c61b581 --- /dev/null +++ b/docs/package-lock.json @@ -0,0 +1,3211 @@ +{ + "name": "twinbasic-docs", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "twinbasic-docs", + "version": "0.0.0", + "devDependencies": { + "pagedjs-cli": "^0.4.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/polyfill": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.12.1.tgz", + "integrity": "sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==", + "deprecated": "🚨 This package has been deprecated in favor of separate inclusion of a polyfill and regenerator-runtime (when needed). See the @babel/polyfill docs (https://babeljs.io/docs/en/babel-polyfill) for more information.", + "dev": true, + "license": "MIT", + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@pdf-lib/standard-fonts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", + "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.6" + } + }, + "node_modules/@pdf-lib/upng": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz", + "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.10" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", + "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "4.3.4", + "extract-zip": "2.0.1", + "progress": "2.0.3", + "proxy-agent": "6.3.0", + "tar-fs": "3.0.4", + "unbzip2-stream": "1.4.3", + "yargs": "17.7.1" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.8.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", + "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-events": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.3.tgz", + "integrity": "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.1.tgz", + "integrity": "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", + "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.1.tgz", + "integrity": "sha512-Vp0cnjYyrEC4whYTymQ+YZi6pBpfiICZO3cfRG8sy67ZNWe951urv1x4eW1BKNngw3U+3fPYb5JQvHbCtxH7Ow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.3.tgz", + "integrity": "sha512-Kccpc7ACfXaxfeInfqKcZtW4pT5YBn1mesc4sCsun6sRwtbJ4h+sNOaksUpYEJUKfN65YWC6Bw2OJEFiKxq8nQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chromium-bidi": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", + "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "3.0.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/clear-cut": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clear-cut/-/clear-cut-2.0.2.tgz", + "integrity": "sha512-WVgn/gSejQ+0aoR8ucbKIdo6icduPZW6AbWwyUmAUgxy63rUYjwa5rj/HeoNPhf0/XPrl82X8bO/hwBkSmsFtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1147663", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", + "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "dev": true, + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extract-zip/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/extract-zip/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/katex": { + "version": "0.16.46", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.46.tgz", + "integrity": "sha512-WHy4Coo+bGZyH7NwJKHkS04YFsFcarWbAEOAC3EMndzdN6VSZqklLLIgfxzyaW9jDoeGYJX9SWbJPKpecox0Uw==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathjax": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz", + "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/netmask": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.1.tgz", + "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.0.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.6.1", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.1.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "strip-ansi": "^7.0.1", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pagedjs": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/pagedjs/-/pagedjs-0.4.3.tgz", + "integrity": "sha512-YtAN9JAjsQw1142gxEjEAwXvOF5nYQuDwnQ67RW2HZDkMLI+b4RsBE37lULZa9gAr6kDAOGBOhXI4wGMoY3raw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/polyfill": "^7.10.1", + "@babel/runtime": "^7.21.0", + "clear-cut": "^2.0.2", + "css-tree": "^1.1.3", + "event-emitter": "^0.3.5" + } + }, + "node_modules/pagedjs-cli": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/pagedjs-cli/-/pagedjs-cli-0.4.3.tgz", + "integrity": "sha512-NwmRwLiQAjXBdfG/tUbbv2iA8sZPiXRovTd4YxQRmYpMV1QQufg9Pqni0u6MMFqGxz6W2aZFdxZ5jPjtWrTqXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^11.0.0", + "express": "^4.18.2", + "html-entities": "^2.4.0", + "katex": "^0.16.8", + "lodash": "^4.17.21", + "mathjax": "^3.2.2", + "node-fetch": "^3.3.1", + "ora": "^6.3.1", + "pagedjs": "^0.4.3", + "pdf-lib": "1.17.1", + "puppeteer": "^20.9.0", + "replace-ext": "^2.0.0" + }, + "bin": { + "pagedjs-cli": "src/cli.js" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pdf-lib": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz", + "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pdf-lib/standard-fonts": "^1.0.0", + "@pdf-lib/upng": "^1.0.1", + "pako": "^1.0.11", + "tslib": "^1.11.1" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-agent": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", + "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/puppeteer": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.9.0.tgz", + "integrity": "sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw==", + "deprecated": "< 24.15.0 is no longer supported", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "1.4.6", + "cosmiconfig": "8.2.0", + "puppeteer-core": "20.9.0" + }, + "engines": { + "node": ">=16.3.0" + } + }, + "node_modules/puppeteer-core": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", + "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "1.4.6", + "chromium-bidi": "0.4.16", + "cross-fetch": "4.0.0", + "debug": "4.3.4", + "devtools-protocol": "0.0.1147663", + "ws": "8.13.0" + }, + "engines": { + "node": ">=16.3.0" + }, + "peerDependencies": { + "typescript": ">= 4.7.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true, + "license": "MIT" + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "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/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tar-fs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", + "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/unbzip2-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", + "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..f580057c --- /dev/null +++ b/docs/package.json @@ -0,0 +1,9 @@ +{ + "name": "twinbasic-docs", + "version": "0.0.0", + "private": true, + "description": "PDF book pipeline for the twinBASIC documentation", + "devDependencies": { + "pagedjs-cli": "^0.4.3" + } +} From e397c517efec3c7d805fc05e480bf6ff23f9323c Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Fri, 15 May 2026 19:06:47 +0200 Subject: [PATCH 02/27] Render without justthedocs styling. Add custom styling for the print target. --- docs/_layouts/book.html | 3 +- docs/assets/css/print.css | 248 ++++++++++++++++++++++++++++++++++++++ docs/assets/css/rouge.css | 116 ++++++++++++++++++ 3 files changed, 366 insertions(+), 1 deletion(-) create mode 100644 docs/assets/css/print.css create mode 100644 docs/assets/css/rouge.css diff --git a/docs/_layouts/book.html b/docs/_layouts/book.html index c6aed015..6b4934e7 100644 --- a/docs/_layouts/book.html +++ b/docs/_layouts/book.html @@ -3,7 +3,8 @@ {{ page.title }} β€” {{ site.title }} - + +
diff --git a/docs/assets/css/print.css b/docs/assets/css/print.css new file mode 100644 index 00000000..505aeaff --- /dev/null +++ b/docs/assets/css/print.css @@ -0,0 +1,248 @@ +/* Self-contained print stylesheet for the twinBASIC documentation PDF. + Rendered through pagedjs-cli (CSS Paged Media Module Level 3). + + Paired with rouge.css (Rouge `github.light` theme, generated via rougify). + No just-the-docs styles are loaded for the PDF build --- this file is the + complete book design. */ + + +/* ---- Page geometry, running header, page numbers --------------------- */ + +@page { + size: A4; + margin: 22mm 20mm 22mm 20mm; + + @bottom-right { + content: counter(page); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + font-size: 9pt; + color: #555; + } + + @top-right { + content: string(chapter-title); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + font-size: 9pt; + color: #555; + } +} + +/* First page of each chapter: drop the running header (the H1 is right there). */ +@page :first { + @top-right { content: ""; } +} + + +/* ---- Chapter boundaries --------------------------------------------- */ + +article.page > h1:first-of-type { + string-set: chapter-title content(); + break-before: page; +} + +/* Don't force a page break before the very first chapter. */ +article.page:first-of-type > h1:first-of-type { + break-before: avoid; +} + + +/* ---- Base typography ----------------------------------------------- */ + +html { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 10.5pt; + line-height: 1.45; + color: #1a1a1a; +} + +body { + margin: 0; +} + +strong { font-weight: 700; } +em { font-style: italic; } + + +/* ---- Headings ------------------------------------------------------ */ + +h1, h2, h3, h4, h5, h6 { + font-family: inherit; + color: #111; + margin: 1.6em 0 0.5em; + line-height: 1.25; + break-after: avoid; + break-inside: avoid; +} + +h1 { + font-size: 24pt; + font-weight: 700; + margin-top: 0; + margin-bottom: 0.8em; +} + +h2 { + font-size: 18pt; + font-weight: 700; + border-bottom: 0.5pt solid #bbb; + padding-bottom: 0.2em; +} + +h3 { font-size: 14pt; font-weight: 600; } +h4 { font-size: 12pt; font-weight: 600; } +h5 { font-size: 11pt; font-weight: 600; } +h6 { font-size: 10.5pt; font-weight: 600; color: #444; } + + +/* ---- Paragraphs and lists ----------------------------------------- */ + +p { + margin: 0.6em 0; + orphans: 2; + widows: 2; +} + +ul, ol { + margin: 0.5em 0; + padding-left: 1.8em; +} + +li { + margin: 0.15em 0; + orphans: 2; + widows: 2; +} + + +/* ---- Definition lists (used heavily for parameter docs) ----------- */ + +dl { margin: 0.6em 0; } +dt { font-weight: 600; margin-top: 0.4em; } +dd { margin: 0.1em 0 0.4em 1.8em; } + + +/* ---- Inline code -------------------------------------------------- */ + +code { + font-family: "Cascadia Mono", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 0.92em; + background: #f4f4f6; + border: 0.5pt solid #e0e0e4; + border-radius: 2pt; + padding: 0 0.25em; +} + + +/* ---- Code blocks -------------------------------------------------- */ + +pre, .highlight { + font-family: "Cascadia Mono", Consolas, "Liberation Mono", Menlo, monospace; + font-size: 9pt; + line-height: 1.4; + margin: 0.6em 0; + padding: 0.6em 0.8em; + border-radius: 3pt; + white-space: pre-wrap; + word-wrap: break-word; + overflow-wrap: break-word; + break-inside: auto; +} + +/* Code inside
 shouldn't get the inline-code box/border. */
+pre code, .highlight code {
+  background: none;
+  border: none;
+  padding: 0;
+  font-size: inherit;
+  white-space: inherit;
+}
+
+/* Strip the nested `.highlighter-rouge > .highlight > pre.highlight`
+   triple-wrapper backgrounds; the outer wrapper keeps the colour. */
+.highlighter-rouge .highlight,
+.highlighter-rouge pre.highlight {
+  background: transparent;
+  padding: 0;
+  margin: 0;
+}
+
+
+/* ---- Links -------------------------------------------------------- */
+
+a, a:link, a:visited {
+  color: #1d4ed8;
+  text-decoration: none;
+}
+
+
+/* ---- Tables ------------------------------------------------------- */
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  margin: 0.8em 0;
+  font-size: 0.95em;
+  break-inside: auto;
+}
+
+th, td {
+  border: 0.5pt solid #ccc;
+  padding: 0.3em 0.5em;
+  text-align: left;
+  vertical-align: top;
+}
+
+th {
+  background: #f4f4f6;
+  font-weight: 600;
+}
+
+
+/* ---- Blockquotes -------------------------------------------------- */
+
+blockquote {
+  border-left: 2pt solid #bbb;
+  margin: 0.6em 0;
+  padding: 0.2em 0 0.2em 0.8em;
+  color: #444;
+}
+
+
+/* ---- GFMA admonitions (NOTE / IMPORTANT / WARNING / CAUTION / TIP) - */
+
+.markdown-alert {
+  border-left: 3pt solid #888;
+  background: #f6f8fa;
+  margin: 0.8em 0;
+  padding: 0.4em 0.8em 0.5em;
+  break-inside: avoid;
+}
+
+.markdown-alert-title {
+  font-weight: 700;
+  margin: 0 0 0.3em;
+  display: flex;
+  align-items: center;
+  gap: 0.3em;
+}
+
+.markdown-alert-title svg {
+  width: 11pt;
+  height: 11pt;
+  fill: currentColor;
+  flex-shrink: 0;
+}
+
+.markdown-alert > :last-child { margin-bottom: 0; }
+
+.markdown-alert-note         { border-left-color: #0969da; }
+.markdown-alert-note         .markdown-alert-title { color: #0969da; }
+.markdown-alert-important    { border-left-color: #8250df; }
+.markdown-alert-important    .markdown-alert-title { color: #8250df; }
+.markdown-alert-warning      { border-left-color: #bf8700; }
+.markdown-alert-warning      .markdown-alert-title { color: #bf8700; }
+.markdown-alert-caution      { border-left-color: #cf222e; }
+.markdown-alert-caution      .markdown-alert-title { color: #cf222e; }
+.markdown-alert-tip          { border-left-color: #1a7f37; }
+.markdown-alert-tip          .markdown-alert-title { color: #1a7f37; }
diff --git a/docs/assets/css/rouge.css b/docs/assets/css/rouge.css
new file mode 100644
index 00000000..dfebb54b
--- /dev/null
+++ b/docs/assets/css/rouge.css
@@ -0,0 +1,116 @@
+.highlight table td { padding: 5px; }
+.highlight table pre { margin: 0; }
+.highlight, .highlight .w {
+  color: #24292f;
+  background-color: #f6f8fa;
+}
+.highlight .k, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kt, .highlight .kv {
+  color: #cf222e;
+}
+.highlight .gr {
+  color: #f6f8fa;
+}
+.highlight .gd {
+  color: #82071e;
+  background-color: #ffebe9;
+}
+.highlight .nb {
+  color: #953800;
+}
+.highlight .nc {
+  color: #953800;
+}
+.highlight .no {
+  color: #953800;
+}
+.highlight .nn {
+  color: #953800;
+}
+.highlight .sr {
+  color: #116329;
+}
+.highlight .na {
+  color: #116329;
+}
+.highlight .nt {
+  color: #116329;
+}
+.highlight .gi {
+  color: #116329;
+  background-color: #dafbe1;
+}
+.highlight .ges {
+  font-weight: bold;
+  font-style: italic;
+}
+.highlight .kc {
+  color: #0550ae;
+}
+.highlight .l, .highlight .ld, .highlight .m, .highlight .mb, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo, .highlight .mx {
+  color: #0550ae;
+}
+.highlight .sb {
+  color: #0550ae;
+}
+.highlight .bp {
+  color: #0550ae;
+}
+.highlight .ne {
+  color: #0550ae;
+}
+.highlight .nl {
+  color: #0550ae;
+}
+.highlight .py {
+  color: #0550ae;
+}
+.highlight .nv, .highlight .vc, .highlight .vg, .highlight .vi, .highlight .vm {
+  color: #0550ae;
+}
+.highlight .o, .highlight .ow {
+  color: #0550ae;
+}
+.highlight .gh {
+  color: #0550ae;
+  font-weight: bold;
+}
+.highlight .gu {
+  color: #0550ae;
+  font-weight: bold;
+}
+.highlight .s, .highlight .sa, .highlight .sc, .highlight .dl, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .sx, .highlight .s1, .highlight .ss {
+  color: #0a3069;
+}
+.highlight .nd {
+  color: #8250df;
+}
+.highlight .nf, .highlight .fm {
+  color: #8250df;
+}
+.highlight .err {
+  color: #f6f8fa;
+  background-color: #82071e;
+}
+.highlight .c, .highlight .ch, .highlight .cd, .highlight .cm, .highlight .cp, .highlight .cpf, .highlight .c1, .highlight .cs {
+  color: #6e7781;
+}
+.highlight .gl {
+  color: #6e7781;
+}
+.highlight .gt {
+  color: #6e7781;
+}
+.highlight .ni {
+  color: #24292f;
+}
+.highlight .si {
+  color: #24292f;
+}
+.highlight .ge {
+  color: #24292f;
+  font-style: italic;
+}
+.highlight .gs {
+  color: #24292f;
+  font-weight: bold;
+}

From 93a5dd462306af33b7cf16aaa6c7421925cfc592 Mon Sep 17 00:00:00 2001
From: Kuba Sunderland-Ober 
Date: Fri, 15 May 2026 19:52:50 +0200
Subject: [PATCH 03/27] Add basic book concatenator.

---
 docs/_data/book.yml              | 34 ++++++++++++++++++++++++++++++++
 docs/_layouts/book-combined.html | 12 +++++++++++
 docs/book.html                   | 15 ++++++++++++++
 3 files changed, 61 insertions(+)
 create mode 100644 docs/_data/book.yml
 create mode 100644 docs/_layouts/book-combined.html
 create mode 100644 docs/book.html

diff --git a/docs/_data/book.yml b/docs/_data/book.yml
new file mode 100644
index 00000000..0ee402c5
--- /dev/null
+++ b/docs/_data/book.yml
@@ -0,0 +1,34 @@
+# Chapter manifest for the twinBASIC documentation PDF.
+#
+# `chapters` is an ordered list of page URLs (matching each page's
+# `permalink:` frontmatter). The book.html iterator looks each entry up
+# in `site.pages`, drops the page's rendered `content` inside an
+# `
` wrapper, and lets pagedjs-cli paginate the +# concatenated result. + +chapters: + - /tB/Modules/Interaction/ + - /tB/Modules/Interaction/AppActivate + - /tB/Modules/Interaction/Beep + - /tB/Modules/Interaction/CallByDispId + - /tB/Modules/Interaction/CallByName + - /tB/Modules/Interaction/Choose + - /tB/Modules/Interaction/Command + - /tB/Modules/Interaction/CreateObject + - /tB/Modules/Interaction/DeleteSetting + - /tB/Modules/Interaction/DoEvents + - /tB/Modules/Interaction/Environ + - /tB/Modules/Interaction/GetAllSettings + - /tB/Modules/Interaction/GetObject + - /tB/Modules/Interaction/GetSetting + - /tB/Modules/Interaction/IIf + - /tB/Modules/Interaction/If + - /tB/Modules/Interaction/InputBox + - /tB/Modules/Interaction/MsgBox + - /tB/Modules/Interaction/Partition + - /tB/Modules/Interaction/RaiseEventByName + - /tB/Modules/Interaction/RaiseEventByName2 + - /tB/Modules/Interaction/SaveSetting + - /tB/Modules/Interaction/SendKeys + - /tB/Modules/Interaction/Shell + - /tB/Modules/Interaction/Switch diff --git a/docs/_layouts/book-combined.html b/docs/_layouts/book-combined.html new file mode 100644 index 00000000..061b1b52 --- /dev/null +++ b/docs/_layouts/book-combined.html @@ -0,0 +1,12 @@ + + + + + {{ site.title }} + + + + +{{ content }} + + diff --git a/docs/book.html b/docs/book.html new file mode 100644 index 00000000..790628c0 --- /dev/null +++ b/docs/book.html @@ -0,0 +1,15 @@ +--- +layout: book-combined +permalink: /book.html +sitemap: false +--- +{% for chapter_url in site.data.book.chapters -%} + {%- assign chapter = site.pages | where: "url", chapter_url | first -%} + {%- if chapter %} +
+{{ chapter.content | markdownify }} +
+ {%- else %} + + {%- endif %} +{%- endfor %} From 6552361fcffe5fe50b11286d83062131be086788 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Fri, 15 May 2026 20:01:51 +0200 Subject: [PATCH 04/27] Add a simple build script, use local stylesheet links. --- docs/_layouts/book-combined.html | 4 ++-- docs/book.bat | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/book.bat diff --git a/docs/_layouts/book-combined.html b/docs/_layouts/book-combined.html index 061b1b52..00b0b79d 100644 --- a/docs/_layouts/book-combined.html +++ b/docs/_layouts/book-combined.html @@ -3,8 +3,8 @@ {{ site.title }} - - + + {{ content }} diff --git a/docs/book.bat b/docs/book.bat new file mode 100644 index 00000000..8a2447a1 --- /dev/null +++ b/docs/book.bat @@ -0,0 +1,4 @@ +@echo off +bundle exec jekyll build --config _config.yml,_config-pdf.yml || exit /b +if not exist _pdf mkdir _pdf +npx pagedjs-cli _site-pdf\book.html -o _pdf\book.pdf --outline-tags h1,h2,h3 -t 180000 From 937046db26a0e179be2469a8d61d32b81dabb726 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Fri, 15 May 2026 21:52:26 +0200 Subject: [PATCH 05/27] Remove the inapplicable markdownify filter. --- docs/_data/book.yml | 53 ++++++++++++++++----------------------- docs/assets/css/print.css | 52 ++++++++++++++++++++++++++------------ docs/book.html | 19 ++++++++------ 3 files changed, 70 insertions(+), 54 deletions(-) diff --git a/docs/_data/book.yml b/docs/_data/book.yml index 0ee402c5..204603f6 100644 --- a/docs/_data/book.yml +++ b/docs/_data/book.yml @@ -1,34 +1,25 @@ # Chapter manifest for the twinBASIC documentation PDF. # -# `chapters` is an ordered list of page URLs (matching each page's -# `permalink:` frontmatter). The book.html iterator looks each entry up -# in `site.pages`, drops the page's rendered `content` inside an -# `
` wrapper, and lets pagedjs-cli paginate the -# concatenated result. +# `sections` is an ordered list of URL prefixes. The book.html iterator +# expands each prefix into every page in `site.pages` whose URL starts +# with it, sorted by URL. Each page's `
` becomes +# a chapter; pagedjs-cli paginates the concatenated result. +# +# Index pages (URL ending in `/`) sort before sibling content pages +# under ASCII lexicographic order, which gives a natural +# "package landing β†’ individual symbols" reading flow. -chapters: - - /tB/Modules/Interaction/ - - /tB/Modules/Interaction/AppActivate - - /tB/Modules/Interaction/Beep - - /tB/Modules/Interaction/CallByDispId - - /tB/Modules/Interaction/CallByName - - /tB/Modules/Interaction/Choose - - /tB/Modules/Interaction/Command - - /tB/Modules/Interaction/CreateObject - - /tB/Modules/Interaction/DeleteSetting - - /tB/Modules/Interaction/DoEvents - - /tB/Modules/Interaction/Environ - - /tB/Modules/Interaction/GetAllSettings - - /tB/Modules/Interaction/GetObject - - /tB/Modules/Interaction/GetSetting - - /tB/Modules/Interaction/IIf - - /tB/Modules/Interaction/If - - /tB/Modules/Interaction/InputBox - - /tB/Modules/Interaction/MsgBox - - /tB/Modules/Interaction/Partition - - /tB/Modules/Interaction/RaiseEventByName - - /tB/Modules/Interaction/RaiseEventByName2 - - /tB/Modules/Interaction/SaveSetting - - /tB/Modules/Interaction/SendKeys - - /tB/Modules/Interaction/Shell - - /tB/Modules/Interaction/Switch +sections: + - /tB/Core/ + - /tB/Modules/ + - /tB/Packages/VBRUN/ + - /tB/Packages/VB/ + - /tB/Packages/WebView2/ + - /tB/Packages/Assert/ + - /tB/Packages/CustomControls/ + - /tB/Packages/CEF/ + - /tB/Packages/WinEventLogLib/ + - /tB/Packages/WinNamedPipesLib/ + - /tB/Packages/WinServicesLib/ + - /tB/Packages/tbIDE/ + - /tB/Packages/WinNativeCommonCtls/ diff --git a/docs/assets/css/print.css b/docs/assets/css/print.css index 505aeaff..21cd32b0 100644 --- a/docs/assets/css/print.css +++ b/docs/assets/css/print.css @@ -126,45 +126,65 @@ dd { margin: 0.1em 0 0.4em 1.8em; } code { font-family: "Cascadia Mono", Consolas, "Liberation Mono", Menlo, monospace; - font-size: 0.92em; background: #f4f4f6; border: 0.5pt solid #e0e0e4; border-radius: 2pt; - padding: 0 0.25em; + padding: 0 0.2em; + /* Smaller font-size makes the chip visually tight: glyphs fit naturally + inside the em-box rather than being forced into a cropped height. + Negative top/bottom `margin` on the inline-block keeps the chip's + contribution to the line box small so adjacent lines aren't pushed + apart. */ + display: inline-block; + line-height: 1; + margin: -0.1em 0; + vertical-align: middle; } -/* ---- Code blocks -------------------------------------------------- */ +/* ---- Code blocks -------------------------------------------------- + Two paths: + - Plain
 (no language fence): the 
 carries the box.
+   - Rouge-rendered (div.highlighter-rouge > .highlight > pre.highlight > code):
+     the div.highlighter-rouge wrapper carries the box; all nested elements
+     (.highlight, pre.highlight, the whitespace `.w` spans whose background
+     rouge.css would otherwise paint behind indented lines) are stripped. */
 
-pre, .highlight {
+pre,
+div.highlighter-rouge {
   font-family: "Cascadia Mono", Consolas, "Liberation Mono", Menlo, monospace;
   font-size: 9pt;
   line-height: 1.4;
   margin: 0.6em 0;
   padding: 0.6em 0.8em;
+  background: #f6f8fa;
   border-radius: 3pt;
+  break-inside: auto;
   white-space: pre-wrap;
   word-wrap: break-word;
   overflow-wrap: break-word;
-  break-inside: auto;
 }
 
-/* Code inside 
 shouldn't get the inline-code box/border. */
-pre code, .highlight code {
+div.highlighter-rouge pre,
+div.highlighter-rouge .highlight,
+div.highlighter-rouge pre.highlight,
+div.highlighter-rouge .highlight .w {
+  background: transparent;
+  padding: 0;
+  margin: 0;
+}
+
+/* Code inside 
 shouldn't get the inline-code box/border, and must
+   revert the inline-block + tight line-height the chip rule introduces. */
+pre code,
+div.highlighter-rouge code {
   background: none;
   border: none;
   padding: 0;
   font-size: inherit;
   white-space: inherit;
-}
-
-/* Strip the nested `.highlighter-rouge > .highlight > pre.highlight`
-   triple-wrapper backgrounds; the outer wrapper keeps the colour. */
-.highlighter-rouge .highlight,
-.highlighter-rouge pre.highlight {
-  background: transparent;
-  padding: 0;
-  margin: 0;
+  display: inline;
+  line-height: inherit;
 }
 
 
diff --git a/docs/book.html b/docs/book.html
index 790628c0..e68f4e7f 100644
--- a/docs/book.html
+++ b/docs/book.html
@@ -3,13 +3,18 @@
 permalink: /book.html
 sitemap: false
 ---
-{% for chapter_url in site.data.book.chapters -%}
-  {%- assign chapter = site.pages | where: "url", chapter_url | first -%}
-  {%- if chapter %}
+{%- for section in site.data.book.sections -%}
+  {%- assign chapters = site.pages | where_exp: "p", "p.url contains section" | sort: "url" -%}
+  {%- for chapter in chapters -%}
+    {%- comment %}
+      Strip the leading slash from absolute `src="/..."` attributes so images
+      resolve relative to _site-pdf/book.html (which is what pagedjs-cli reads
+      under file://). Source pages use relative paths in markdown, but the
+      site rewrites them to absolute-from-root for the regular browser site.
+    {% endcomment -%}
+    {%- assign body = chapter.content | replace: 'src="/', 'src="' %}
 
-{{ chapter.content | markdownify }} +{{ body }}
- {%- else %} - - {%- endif %} + {%- endfor -%} {%- endfor %} From 3b62cfd1c6f26d7e3916b15b3a628c5056480049 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Fri, 15 May 2026 23:27:39 +0200 Subject: [PATCH 06/27] Work around the pagedjs space-squashing bug. --- docs/assets/css/print.css | 2 +- docs/book.html | 52 +++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/assets/css/print.css b/docs/assets/css/print.css index 21cd32b0..f41b479d 100644 --- a/docs/assets/css/print.css +++ b/docs/assets/css/print.css @@ -159,10 +159,10 @@ div.highlighter-rouge { padding: 0.6em 0.8em; background: #f6f8fa; border-radius: 3pt; - break-inside: auto; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; + break-inside: auto; } div.highlighter-rouge pre, diff --git a/docs/book.html b/docs/book.html index e68f4e7f..4bc8f301 100644 --- a/docs/book.html +++ b/docs/book.html @@ -3,16 +3,54 @@ permalink: /book.html sitemap: false --- +{%- comment -%} + Whitespace primitives. `sp` is one space; `nl` is one newline, + captured between the opening and closing capture tags so the LF is + preserved verbatim (trim markers strip only outside-the-capture + whitespace). + + Inter-span whitespace patterns: PagedJS drops bare text-node + whitespace between sibling elements when it splits a pre across + pages, mashing tokens and collapsing line breaks. Wrapping each + inter-span whitespace run in a span.w element makes it a structured + child that survives the split. + + Variants, longest first so each consumes its bytes before a shorter + pattern can fragment them: + SP NL SP NL -- blank line between a code-line ending (trailing + space outside the last token's span) and the next. + NL SP NL -- blank line between a comment-line ending (trailing + space already inside the comment span) and the next. + SP NL -- code line directly followed by the next line. + NL -- comment line directly followed by the next line. +{%- endcomment -%} + +{%- assign sp = " " -%} +{%- capture nl %} +{% endcapture -%} + +{%- assign p1_search = '' | append: sp | append: nl | append: sp | append: nl | append: ' {{ body }}
From 21dbcf2864f6af2376438311ecfcd132615fdc39 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 16 May 2026 11:37:53 +0200 Subject: [PATCH 07/27] Prevent markdown contents from spilling into the renderer. --- docs/book.html | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/book.html b/docs/book.html index 4bc8f301..544701af 100644 --- a/docs/book.html +++ b/docs/book.html @@ -44,7 +44,22 @@ {%- for section in site.data.book.sections -%} {%- assign chapters = site.pages | where_exp: "p", "p.url contains section" | sort: "url" -%} {%- for chapter in chapters -%} - {%- assign body = chapter.content + {%- comment -%} + Jekyll's `chapter.content` returns rendered HTML for pages whose + Renderer#run has already converted them, but raw markdown for + pages whose conversion happens after book.html's. The state is a + function of Jekyll's internal rendering order and not stable + across builds. Detect by leading character and markdownify when + we still have raw source -- kramdown's `parse_block_html: true` + passes pre-rendered HTML through unchanged. + {%- endcomment -%} + {%- assign first_char = chapter.content | strip | slice: 0, 1 -%} + {%- if first_char == '<' -%} + {%- assign body = chapter.content -%} + {%- else -%} + {%- assign body = chapter.content | markdownify -%} + {%- endif -%} + {%- assign body = body | replace: 'src="/', 'src="' | replace: p1_search, p1_replace | replace: p2_search, p2_replace From 4a39f9a687429cd4f7a53aabf9ad0e8f03232b90 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 16 May 2026 11:48:37 +0200 Subject: [PATCH 08/27] Write a plan for remaining work. --- BOOKPLAN.md | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 BOOKPLAN.md diff --git a/BOOKPLAN.md b/BOOKPLAN.md new file mode 100644 index 00000000..6388607a --- /dev/null +++ b/BOOKPLAN.md @@ -0,0 +1,267 @@ +# PDF Book Build β€” Plan + +Currently `book.bat` produces a ~1500-page PDF that is a flat concatenation of every reference page. It works, but it doesn't read like a book β€” no front matter, no parts, no global TOC, every chapter is `

`, and "See Also" cross-references point at standalone `file://` URLs rather than jumping within the PDF. + +This file is the staged plan for turning that output into an actual book. Phases are independent; each one ends in a verifiable rendered artefact and is a reasonable commit boundary. + +## Pipeline recap + +From `docs/`: + +``` +bundle exec jekyll build --config _config.yml,_config-pdf.yml +npx pagedjs-cli _site-pdf/book.html -o _pdf/book.pdf --outline-tags h1,h2,h3 -t 600000 +``` + +or `book.bat`. Touch points: + +- [docs/book.html](docs/book.html) β€” iterator that concatenates every chapter into one HTML document. Liquid filters here transform chapter content before emission. +- [docs/_layouts/book-combined.html](docs/_layouts/book-combined.html) β€” wraps book.html in `` and links rouge.css + print.css. +- [docs/_layouts/book.html](docs/_layouts/book.html) β€” minimal per-page wrapper used when each source page is rendered to its own `_site-pdf/.html`. The combined book.html iterates over those rendered pages via Jekyll's `site.pages` collection. +- [docs/assets/css/print.css](docs/assets/css/print.css) β€” the book's design (page geometry, headings, code blocks, tables, admonitions, running header). +- [docs/_data/book.yml](docs/_data/book.yml) β€” the manifest book.html iterates over. Currently a flat `sections:` list of URL prefixes. +- [docs/_config-pdf.yml](docs/_config-pdf.yml) β€” overlay config that switches the default layout to `book` and the output directory to `_site-pdf`. + +## Phase 1 β€” Structural framing + +Goal: cover β†’ colophon β†’ Part I divider β†’ Part I chapters β†’ Part II divider β†’ … reads like a book's table of contents shape even before a real TOC exists. + +### 1.1 Schema upgrade for `_data/book.yml` + +Replace the flat `sections:` list with `parts:`. Each part has: + +- `title` β€” e.g. "The VBRUN Package". +- `subtitle` β€” optional, e.g. "Runtime types for controls, errors, and the property bag". +- `prefixes` β€” URL prefixes that contribute chapters, equivalent to today's `sections` entries. +- `intro` β€” optional Markdown blob used on the divider page. Defaults to the first paragraph of the package's `index.md`. + +Sketch: + +```yaml +parts: + - title: "The Core Language" + subtitle: "Statements, operators, and built-in keywords" + prefixes: [/tB/Core/] + - title: "The VBA Runtime" + subtitle: "Standard runtime modules β€” Strings, Math, FileSystem, …" + prefixes: [/tB/Modules/] + - title: "The VBRUN Package" + prefixes: [/tB/Packages/VBRUN/] + … +``` + +13 parts total, one per package (Core, VBA, VBRUN, VB, WebView2, Assert, CustomControls, CEF, WinEventLogLib, WinNamedPipesLib, WinServicesLib, tbIDE, WinNativeCommonCtls). The intro paragraph for each is sourced from the package's existing `index.md`. + +### 1.2 Part divider pages + +Emit, before each part's chapters, an `
` block: + +```html +
+

Part {{ part_index_roman }}

+

{{ part.title }}

+

{{ part.subtitle }}

+
{{ part.intro | markdownify }}
+
+``` + +CSS in print.css: + +- `break-before: page`, `break-after: page` on `.part-divider`. +- Center vertically, large display type for `h1`, italic subtitle. +- Suppress the running header on divider pages (`@page :first` rule keyed off a CSS string). + +### 1.3 Title page + +Front-matter page 1. A single `
` with: + +- The book title β€” "twinBASIC Documentation". +- A subtitle line β€” "Reference Manual & Tutorials". +- The build date and short commit hash, sourced from `site.time` and a `git` shell-out captured into a Jekyll data file (or hard-coded in `book.yml`). +- Copyright/attribution line. + +Image (logo) optional β€” `docs/favicon.png` exists but is small. A larger source asset would be nice but is not blocking. + +### 1.4 Colophon page + +Front-matter page 2. Pulls together: + +- Site copyright (already in `_config.yml` as `footer_content`). +- The CC-BY-4.0 attribution that VBA-derived pages currently emit via `_includes/footer_custom.html`. Promote it to a single book-wide notice. +- Build provenance β€” Jekyll version, pagedjs-cli version, the `commit-hash@date` from 1.3. + +### 1.5 Heading hierarchy shift + +Today every chapter's first heading is `

` because each source page's `# Title` becomes a top-level heading. In a book this should be `

` so the Part divider's `

` is the only H1 per part. + +Mechanism: a Liquid pass in book.html that downgrades headings inside each chapter body: + +```liquid +{%- assign body = body + | replace: '', '' + | replace: '

', '

' + … +-%} +``` + +`h6` becomes a placeholder tag because there's no `h7`; the placeholder gets styled like `h6` would have been, or simply stripped. Verify which kramdown depths actually appear before deciding β€” most reference pages stop at `### Subsection`. + +print.css updates: + +- `string-set: chapter-title content()` moves from `h1:first-of-type` to `h2:first-of-type`. +- `break-before: page` moves likewise. +- `article.page:first-of-type > h1:first-of-type { break-before: avoid; }` becomes `… > h2:first-of-type`. +- The "first chapter of a part" rule needs `break-before: avoid` on `.part-divider + article.page > h2:first-of-type` so the first chapter doesn't add a redundant page break after the divider. + +### Verification + +Render the PDF. Page 1 is the title page, page 2 is the colophon, page 3 is "Part I: The Core Language", page 4 onwards is Core chapters starting with the existing first Core page (currently AddressOf operator). Running header on chapter pages shows the chapter title; absent on divider pages and the title/colophon pages. + +## Phase 2 β€” In-PDF cross-references + +Goal: clicking "[SetData](SetData)" inside a "See Also" jumps to the SetData chapter in the PDF, not to `file://.../tB/Packages/VBRUN/DataObject/SetData.html`. + +### 2.1 Permalink β†’ in-book anchor map + +In book.html, before the chapter loop, build a Liquid map from each chapter's resolved URL to a stable in-book anchor ID. + +Two ID-format choices: + +- **Counter-style**: `ch-0001`, `ch-0002`, … assigned in iteration order. Compact; opaque; survives URL rewrites. Easier to debug if the count is reasonable. +- **Path-style**: derive from the permalink, e.g. `/tB/Packages/VBRUN/DataObject/SetData` β†’ `ch-tB-Packages-VBRUN-DataObject-SetData`. Debuggable; long; reveals structure. + +Path-style is the better default β€” debugging "why does this link not resolve" is much easier when the anchor name is readable. + +Build the map by iterating the same `chapters` collection book.html already iterates, capturing `{ url β†’ anchor_id }` in a Liquid object. Liquid doesn't have hash literals, so we use parallel arrays (`urls`, `anchors`) and `array | index_of: url`. Alternative: emit each chapter with a known-format `id=` and let the cross-reference rewrite logic re-derive it from the href target. + +### 2.2 Inject the anchor onto each chapter's first heading + +Right before `

` (the chapter title after Phase 1's hierarchy shift), prepend `` or splice `id="ch-..."` into the `

` tag itself. Splicing is cleaner β€” no empty ``. + +### 2.3 Rewrite chapter-content href attributes + +For each chapter body, after markdownify and the inter-span whitespace replacements: + +- Find `` patterns where `X` doesn't start with `http`, `mailto:`, `#`, or `/`. +- Resolve `X` against the chapter's own URL (so `` from a VBA page resolves to `/tB/Packages/VBRUN/Constants`). +- Look up the resolved URL in the permalink β†’ anchor map. On hit, rewrite to ``. On miss, leave alone (probably broken markdown or a link to a page that didn't make it into the book). + +Liquid can do this with `replace` but the relative-path resolution is the hard part. Options: + +- Pre-build a flat regex of `(absolute-URL) β†’ anchor` and replace per chapter, after first rewriting the relative ``s to absolute. This is tractable if we know the chapter's `permalink` prefix. +- Or generate the absolute hrefs at source time via a small Jekyll filter, but that needs Ruby and is excluded. + +A simpler escape hatch: in Liquid, for each chapter, compute its "URL parent" (everything up to and including the last `/` of its permalink). Pre-pend that to every `` that doesn't start with `http`, `mailto`, `#`, or `/`. Then apply the absolute-URL β†’ anchor replacement. + +Bracket the work β€” Phase 2 has the most "this works on paper but Liquid will hurt" risk. + +### Verification + +- Pick a See Also link (e.g. "SetData" inside "DataObject.GetData"). In the PDF reader, clicking it jumps to the SetData chapter. +- Pick a link that targets a page outside the book (e.g. an external `https://`) β€” confirm it still opens externally. +- Pick a link whose target is a permalink not included in `_data/book.yml` β€” confirm it's left as-is (and document the resulting dead link). + +## Phase 3 β€” Global TOC + +Goal: page 3 (or wherever the front matter ends) is a clickable, page-numbered table of contents listing every part and chapter. + +### 3.1 TOC page + +Emit, after the colophon and before the first part divider, a `

' + | replace: '', '' + | replace: '', '' + | replace: '', '' + | replace: '', '' -%} + + {%- comment -%} + Chapter anchor. For the root URL `/`, the default + slashes-to-dashes derivation collapses to an empty string; use + the front-matter entry's title (slugified) as the seed so the + anchor reads `ch-introduction` rather than just `ch-`. The + plugin uses the same fallback for the URL -> anchor map. + {%- endcomment -%} + {%- if chapter.url == '/' -%} + {%- assign chapter_anchor = 'ch-' | append: fm.title | downcase | replace: ' ', '-' -%} + {%- else -%} + {%- assign url_path = chapter.url | replace: '/', '-' -%} + {%- assign first_char = url_path | slice: 0, 1 -%} + {%- if first_char == '-' -%} + {%- assign url_len = url_path.size | minus: 1 -%} + {%- assign url_path = url_path | slice: 1, url_len -%} + {%- endif -%} + {%- assign last_char = url_path | slice: -1, 1 -%} + {%- if last_char == '-' -%} + {%- assign url_len = url_path.size | minus: 1 -%} + {%- assign url_path = url_path | slice: 0, url_len -%} + {%- endif -%} + {%- assign chapter_anchor = 'ch-' | append: url_path -%} + {%- endif -%} + + {%- assign h2_class_prefix = '

+{{ body }} +

+ {%- endfor -%} +{%- endfor -%} +{%- endif -%} + {%- for part in site.data.book.parts -%}

Part {{ roman[forloop.index0] }}

@@ -128,8 +255,25 @@

{{ part.title }}

{{ part.intro | markdownify }}
{%- endif %}
- {%- for prefix in part.prefixes -%} - {%- assign chapters = site.pages | where_exp: "p", "p.url contains prefix" | sort: "url" -%} + {%- comment -%} + Gather the part's chapters once. A part has either `page:` (single + absolute URL, exact match) or `prefixes:` (list of URL prefixes, + contains-match per prefix, union sorted). Pre-2.4 the loop only + handled `prefixes:`; `page:` was added so one-chapter parts like + the FAQ don't have to invent a folder. + {%- endcomment -%} + {%- if part.page -%} + {%- assign chapters = site.pages | where: "url", part.page -%} + {%- elsif part.prefixes -%} + {%- assign chapters = "" | split: "" -%} + {%- for prefix in part.prefixes -%} + {%- assign matched = site.pages | where_exp: "p", "p.url contains prefix" -%} + {%- assign chapters = chapters | concat: matched -%} + {%- endfor -%} + {%- assign chapters = chapters | sort: "url" -%} + {%- else -%} + {%- assign chapters = "" | split: "" -%} + {%- endif -%} {%- comment -%} 1.6a: state for sub-page detection. Index pages (URL ending in `/`) sort before their siblings under ASCII order, so a single-slot @@ -342,5 +486,4 @@

{{ part.title }}

{%- endif -%} {%- endfor -%} - {%- endfor -%} {%- endfor %} From f617dcef351c1daddbf2c58175c127584df94188 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sat, 16 May 2026 23:02:10 +0200 Subject: [PATCH 24/27] Fix pdf bookmarks/outline structure, add chapter title pages for packages. --- BOOKPLAN.md | 58 +++- docs/_data/book.yml | 197 ++++++++----- docs/_includes/book-chapter-body.html | 267 ++++++++++++++++++ docs/_plugins/book-href-rewrite.rb | 87 +++++- docs/assets/css/print.css | 106 +++++++ docs/book.html | 380 +++++++------------------- 6 files changed, 715 insertions(+), 380 deletions(-) create mode 100644 docs/_includes/book-chapter-body.html diff --git a/BOOKPLAN.md b/BOOKPLAN.md index 46d3df20..ff9eb3e4 100644 --- a/BOOKPLAN.md +++ b/BOOKPLAN.md @@ -17,18 +17,19 @@ or `book.bat`. Render is jekyll (~5 s) then pagedjs-cli (~2 min) for the full bo Touch points and what each one already exposes: -- [docs/book.html](docs/book.html) β€” iterator that concatenates every chapter into one HTML document. Permalink `/book.html`, layout `book-combined`. Contains: whitespace-pattern primitives (p1..p4, indent variants) for the pagedjs whitespace fix; the Roman numerals array; the title-page section (1.3); the front-matter loop (1.7) that emits `site.data.book.front_matter` entries inline between the title page and Part I; the per-part loop with a per-chapter loop nested; sub-page state machine (1.6a) tracking the most recent index URL + kind + name; per-chapter heading depth shift (1.5a + sub-page 1.6b); chapter-anchor + heading-id rewrite (1.5b); compound running-header span emission (1.6c). The per-part chapter loop accepts either `prefixes:` (URL-prefix starts-with match, folder-style sections) or `page:` (exact-URL match, one-chapter parts like the FAQ). Insertion points for new front matter go **after** the title-page section and **before** the `{%- for part in site.data.book.parts -%}` opener. +- [docs/book.html](docs/book.html) β€” iterator that concatenates every chapter into one HTML document. Permalink `/book.html`, layout `book-combined`. Contains: whitespace-pattern primitives (p1..p4, indent variants) for the pagedjs whitespace fix; the Roman numerals array; the title-page section (1.3); the front-matter loop (1.7) that emits `site.data.book.front_matter` entries inline between the title page and Part I; the per-part loop. Each part can be **flat** (a single `chapters` list gathered from `page:` / `prefixes:`) or **chaptered** (1.9; a `foreword_page:` plus a nested `chapters:` list, each with its own divider page). Per-chapter body rendering β€” whitespace fix, heading depth shift, sub-page detection, id uniqueness, header-string emission β€” is factored out into `_includes/book-chapter-body.html` so all three call sites (front-matter, flat part, chaptered part) share one implementation. Insertion points for new front matter go **after** the title-page section and **before** the `{%- for part in site.data.book.parts -%}` opener. +- [docs/_includes/book-chapter-body.html](docs/_includes/book-chapter-body.html) β€” per-chapter body processing, called via `{% include book-chapter-body.html chapter=... %}` from each of book.html's chapter-loop callers. Handles markdownify, the pagedjs whitespace fix (consumes the `p1..p4` patterns from the outer scope), heading depth shift (1.5a + sub-page 1.6b), sub-page detection (1.6a, opt-out via `skip_sub_page_detection`), heading-id uniqueness (1.5b), compound running header (1.6c), and emits the final `
` block. Take-it-or-leave-it parameters cover the cases that don't fit the default: `article_class_override` (front-matter and part-foreword), `chapter_anchor_override` (root URL `/` fallback to `ch-introduction`), `skip_sub_page_detection` (front-matter entries don't share an index hierarchy with following chapters). - [docs/_layouts/book-combined.html](docs/_layouts/book-combined.html) β€” minimal wrapper: `` + `{{ site.title }}` + `rouge.css` + `print.css` + `{{ content }}`. No nav, no JS, no chrome. Pagedjs runs on the rendered output of this layout. - [docs/_layouts/book.html](docs/_layouts/book.html) β€” per-source-page wrapper used when each `Reference/...` page is rendered to its own `_site-pdf/.html`. Not part of the PDF render path itself; the combined `book.html` iterates `site.pages` to gather these as chapters. -- [docs/assets/css/print.css](docs/assets/css/print.css) β€” the book's design. Existing structural rules: `@page` (A4, 22mm margins, running header in `@top-right` via `string(chapter-title)`, page number in `@bottom-right`); `@page :first` (suppresses both β€” used by the title page); `@page divider` (suppresses both, used by part dividers via `page: divider`); `@page front-matter` (suppresses both, used by `article.front-matter` for 1.7 Introduction-style sections); `article { break-before: page }`; per-chapter `string-set: chapter-title` on `article.page > .header-string`; the top-level vs sub-chapter heading-size split (`article.page:not(.sub-chapter) > h2:first-of-type` vs `article.page.sub-chapter > h3:first-of-type`). -- [docs/_data/book.yml](docs/_data/book.yml) β€” the manifest book.html iterates over. Schema: `parts:` is an ordered list of numbered parts (`{ title, subtitle, prefixes }` or `{ title, subtitle, page }`); `front_matter:` is a sibling list of sections (1.7) that emit before Part I with no divider, same per-entry shape as a part. `prefixes:` is a list of URL substrings matched via `contains` (so multiple prefixes can map to a single Part β€” used by 1.8 for the Reference Section and for the VBA Runtime, which has its landing page at `/tB/Packages/VBA` and its members at `/tB/Modules/...`); `page:` is a single exact-URL match (used for one-chapter parts like the FAQ and for the root index in `front_matter:`). Available in Liquid as `site.data.book.parts` and `site.data.book.front_matter`. +- [docs/assets/css/print.css](docs/assets/css/print.css) β€” the book's design. Existing structural rules: `@page` (A4, 22mm margins, running header in `@top-right` via `string(chapter-title)`, page number in `@bottom-right`); `@page :first` (suppresses both β€” used by the title page); `@page divider` (suppresses both, used by part dividers via `page: divider`); `@page front-matter` (suppresses both, used by `article.front-matter` for 1.7 Introduction-style sections); `@page part-foreword` + `@page chapter-divider` (suppresses both, used by the 1.9 part foreword and per-chapter title pages); `article { break-before: page }`; per-chapter `string-set: chapter-title` on `article.page > .header-string`; the top-level vs sub-chapter heading-size split (`article.page:not(.sub-chapter) > h2:first-of-type` vs `article.page.sub-chapter > h3:first-of-type`); chapter-divider H2 typography (`article.chapter-divider h2` β€” 24pt centered, no border) plus its subtitle (`.chapter-subtitle` β€” 13pt italic). +- [docs/_data/book.yml](docs/_data/book.yml) β€” the manifest book.html iterates over. Schema: `parts:` is an ordered list of numbered parts. A flat part has `{ title, subtitle, prefixes }` or `{ title, subtitle, page }`. A **chaptered** part (1.9) has `{ title, subtitle, foreword_page, chapters }`, where `chapters` is an ordered list of per-chapter entries `{ title, subtitle, landing_page, prefixes }`; each chapter emits a full-page `
` title page followed by its landing page (with the source H1 stripped by the plugin) and the prefix-matched content in URL order. `front_matter:` is a sibling list of front-matter sections (1.7), same per-entry shape as a flat part. `prefixes:` is a list of URL substrings matched via `contains` (multiple prefixes can map to one Part / chapter β€” used for the Reference Section in 1.8 and for the VBA chapter's landing at `/tB/Packages/VBA` + members under `/tB/Modules/...`); `page:` is a single exact-URL match. Available in Liquid as `site.data.book.parts` and `site.data.book.front_matter`. - [docs/_data/build.yml](#) β€” **not committed**. Build provenance lives in `site.data.build` (populated in memory by the plugin), so the YAML file is never written. The fields exposed are `site.data.build.commit` (short hash) and `site.data.build.commit_date` (ISO date, `%cs`), or `'unknown'` when git is unavailable. - [docs/_config.yml](docs/_config.yml) β€” the regular-site config. Two fields the book reads: `site.title` ("twinBASIC Documentation") and `site.footer_content` (the canonical copyright string, reused by the title page and colophon). - [docs/_config-pdf.yml](docs/_config-pdf.yml) β€” overlay config layered on top of `_config.yml`. Switches `defaults: layout: book` and `destination: _site-pdf`. The full `defaults:` block has to be restated (Jekyll replaces top-level array keys; it does not merge them). - [docs/_plugins/build-info.rb](docs/_plugins/build-info.rb) β€” captures `git rev-parse --short HEAD` and `git log -1 --format=%cs` into `site.data['build']` on `:site, :post_read`. Falls back to `'unknown'` placeholders when git isn't on PATH. - [docs/_plugins/build-phase-timing.rb](docs/_plugins/build-phase-timing.rb) β€” the cleanest hook-pattern example to copy when writing a new `_plugins/` file (uses every `:site, :hook` boundary). - [docs/_plugins/offlinify.rb](docs/_plugins/offlinify.rb) β€” the offline-site link rewriter; reference example for build-time concerns tightly coupled to Jekyll's URL model. -- [docs/_plugins/book-href-rewrite.rb](docs/_plugins/book-href-rewrite.rb) β€” post-render Ruby pass for Phase 2.2 cross-references. Walks each `
` chapter body in the rendered `book.html`, resolves relative-path hrefs against the chapter's URL parent via `URI.merge` (RFC-3986 path normalization comes from the standard library β€” no manual `../` folding), and rewrites in-book absolute URLs to `#ch-...` anchors using a `Hash` map built from `_data/book.yml` + `site.pages`. The manifest iteration covers both `front_matter:` and `parts:` (1.7), and each entry contributes pages from either `page:` (exact match) or `prefixes:` (starts-with match). The URL β†’ anchor map symmetrizes the trailing-slash form for folder-style indexes **and** the `.html` suffix (pages without explicit `permalink:` end up at `/X.html`; source markdown is inconsistent about which form it writes in cross-references). Out-of-book hrefs emit in their resolved absolute form so they're greppable as `href="/..."` during verification. Hooked into `:pages, :post_render` and filtered to `page.path == "book.html"`; non-book pages incur no cost. Replaces an earlier in-template Liquid implementation (~21 s of render overhead vs ~50 ms here). +- [docs/_plugins/book-href-rewrite.rb](docs/_plugins/book-href-rewrite.rb) β€” post-render Ruby pass for Phase 2.2 cross-references **plus** the 1.9 landing-page H1 strip. Walks each `
` chapter body in the rendered `book.html`, resolves relative-path hrefs against the chapter's URL parent via `URI.merge` (RFC-3986 path normalization from the standard library β€” no manual `../` folding), and rewrites in-book absolute URLs to `#ch-...` anchors using a `Hash` map built from `_data/book.yml` + `site.pages`. The manifest iteration (`book_entries`) covers `front_matter:`, each part's optional `foreword_page:`, flat parts directly, and each chapter inside a chaptered part. The URL β†’ anchor map symmetrizes the trailing-slash form for folder-style indexes **and** the `.html` suffix. For articles whose anchor matches a chapter `landing_page:`, the plugin strips the first `

...

` from the body (after Liquid's heading shift) so the chapter-divider's H2 is the chapter's sole outline entry. Out-of-book hrefs emit in their resolved absolute form so they're greppable as `href="/..."` during verification. Hooked into `:pages, :post_render` and filtered to `page.path == "book.html"`; non-book pages incur no cost. Replaces an earlier in-template Liquid implementation (~21 s of render overhead vs ~50 ms here). - [docs/book.bat](docs/book.bat) β€” invokes `bundle exec jekyll build --config _config.yml,_config-pdf.yml || exit /b` then `npx pagedjs-cli _site-pdf\book.html -o _pdf\book.pdf --outline-tags h1,h2,h3,h4 -t 600000`. Must be run from `cmd.exe`, not PowerShell (see gotchas). ## Build-time tooling policy @@ -374,6 +375,54 @@ Book changes (committed in `_data/book.yml`): - All `href="/tB/Gloss#..."` links from across the book (Core, Modules, Packages) now resolve to in-book `#ch-tB-Gloss-` anchors. Total chapter cross-reference rewrites: **6918** (up from 6181, +737, almost all from Glossary anchors that previously left as broken absolute URLs). PDF page count: **1776** (up from 1714). - Remaining out-of-book absolute links are all legitimate (pages under `/tB/IDE/...` that aren't manifest entries; the `/tB/Packages/` landing page itself, which has no in-book counterpart given the choice to keep packages as separate Parts). +### 1.9 Packages as chapters with full-page title pages + +The 1.8 decision to keep 13 separate per-package Parts proved short-lived once the page count crossed 1700 β€” readers wanted Packages to behave like a single book section, not as a parade of equal-weight Parts. 1.9 collapses the 12 package Parts into one chaptered "Packages" Part, with each package promoted to a chapter (full-page title page) inside it and the `/tB/Packages/` landing recast as the part's foreword. + +#### Schema extension + +A part now has two shapes: + +- **Flat part** (existing) β€” `prefixes:` / `page:` directly on the part. The chapter loop gathers and emits in URL order. Used by Features, FAQ, Tutorials, Core, Reference Section. +- **Chaptered part** (new) β€” `foreword_page:` and a nested `chapters:` list. Each chapter has its own `{ title, subtitle, landing_page, prefixes }`. The part divider opens the section, the foreword page emits as `
` (no running header), and then for each chapter entry the iterator emits a `
` full-page title page followed by the chapter's landing page and prefix-matched content. + +#### Rendering pipeline + +Per-chapter body processing was pulled out of `book.html` into `_includes/book-chapter-body.html` so the three call sites β€” the 1.7 front-matter loop, the flat part chapter loop, and the new chaptered part chapter loop β€” share one implementation. The include takes the chapter via `include.chapter` and a small handful of overrides (`article_class_override`, `chapter_anchor_override`, `skip_sub_page_detection`); the state-machine variables (`current_index_url`, `current_index_kind`, `current_index_name`) live in the caller and the include reads/mutates them in place. The sub-page state machine is reset between each chapter of a chaptered part so each package's class / module folders nest only against their own siblings. + +The chapter divider's content is generated directly in `book.html`, not from a source file β€” `

...

` with the chapter `title` as an H2 and the optional `subtitle` as a `.chapter-subtitle` paragraph. The article id uses the `chd-` prefix (separate from the `ch-` namespace) so the plugin's `
` regex doesn't mistakenly process the divider; the inner `

` carries its own `chd-...-title` id so the PDF outline entry produced by `pagedjs-cli --outline-tags h1,h2,h3,h4` has a valid anchor to jump to. Without an id on the H2 every chapter-divider bookmark collapsed to page 1 -- pagedjs falls back to the document start when an outlinable element has no anchor. + +#### Extra heading shift for chaptered-part chapters + +Every chapter inside a chaptered part receives a third +1 heading depth shift on top of the standard 1.5a shift (and the 1.6b sub-page shift, when applicable). Without this the class / module indexes (a chapter's "top-level" content pages, e.g. `/tB/Packages/VBRUN/AmbientProperties/`) end up at the same outline depth as the chapter divider itself: source-H1 β†’ 1.5a H2 β†’ outline depth 2, identical to the chapter-divider H2. The extra shift demotes those indexes to H3 (outline depth 3, nested below the chapter divider) and their member sub-pages to H4 (outline depth 4) so the outline reads Packages β†’ VBRUN Package β†’ AmbientProperties class β†’ BackColor rather than Packages β†’ VBRUN Package & AmbientProperties class & BackColor & ... all flat. + +The include exposes this as an `extra_heading_shift` parameter and the chaptered-loop call site in `book.html` passes `extra_heading_shift=true`. Chapters in flat parts (Features, FAQ, Tutorials, Core, Reference Section) and front-matter entries continue to use the 1.5a-only shift since they have no chapter divider to nest beneath. Chaptered chapters also pick up an `article.page.chaptered` modifier class so `print.css` can target their now-deeper title and section headings (`article.page.chaptered:not(.sub-chapter) > h3:first-of-type` is the new "big bold chapter title" selector, mirroring the flat-part `> h2:first-of-type` rule one level deeper). + +#### Landing-page H1 strip + +The chapter divider's H2 ("VBA Package") and the chapter landing page's source H1 ("VBA Package") would otherwise emit as two outline entries with the same text. The plugin (`_plugins/book-href-rewrite.rb`) strips the first heading-of-title-level from any article whose anchor matches a chapter's `landing_page:`. Because every chaptered chapter now receives the extra heading shift, the source H1 arrives at the post-render HTML as `

` (1.5a + 1.9 = +2 levels), so the plugin's `FIRST_LANDING_HEADING_REGEX` matches `

` rather than `

`. The strip runs after the Liquid heading shifts, before the href rewrite pass; the landing page's body then opens directly with its second-level content, and the chapter divider's H2 is the chapter's sole H2-level outline entry. + +#### Foreword + +`foreword_page` points at the part's intro URL β€” for Packages this is `/tB/Packages/`, the existing landing that lists the default and built-in packages with one-line descriptors. The foreword emits as `
` between the part divider and the first chapter divider; CSS pins it to a named `part-foreword` page with the running header suppressed. The foreword's anchor is the same URL-derived anchor as any other chapter (`ch-tB-Packages` in this case), so cross-references like `[Packages](/tB/Packages/)` from elsewhere in the book resolve to `#ch-tB-Packages` and land on the foreword. + +#### Verification + +- The book now has **6 numbered parts** (down from 17): Features, FAQ, Tutorials, Core, Reference Section, Packages. +- The Packages part contains **one foreword article** (`ch-tB-Packages`) and **12 chapter dividers** (`chd-tB-Packages-VBA`, ..., `chd-tB-Packages-WinNativeCommonCtls`), one per package, in book.yml order. +- The plugin reports **stripped 12 landing H3s** on every build (one per chaptered chapter); each package's landing article body opens with its first non-title content. +- The PDF outline tree reads `Packages β†’ VBRUN Package β†’ AmbientProperties class β†’ BackColor`, with each level nested one outline depth below its parent (depths 1 β†’ 2 β†’ 3 β†’ 4). Pre-extra-shift the class index and its members both lived at depth 2, side-by-side with the chapter divider. +- Clicking the "VBRUN Package" bookmark in the PDF outline jumps to the chapter-divider page (page 47, 89, or wherever VBRUN's divider lands), not page 1 β€” the `chd-tB-Packages-VBRUN-title` id on the divider's H2 is the anchor target. +- In-book cross-references resolve **6932** (up from 6918). The `/tB/Packages/` foreword anchor now absorbs the previously-broken `href="/tB/Packages/"` links from elsewhere in the book. +- Build wall stays at ~7 s. PDF page count: **1779** (up from 1776 β€” the foreword + 12 chapter-divider pages roughly offset the 11 part-divider pages we no longer emit). + +#### Tradeoffs / open questions + +- **Chapter dividers have a `chd-` id, not `ch-`.** The plugin's article-walking regex matches `ch-` ids only, so chapter dividers are never processed (no href rewrites, no H3 strip needed). That's fine today; if Phase 3's TOC wants to deep-link to chapter dividers it'll need either to consult `chd-` ids directly or to add a parallel anchor namespace. +- **Outline-tag list and depth-5 dropout.** The chaptered extra shift pushes member sub-page section headings (source-H2 inside a sub-page like `BackColor.md`'s `## Example`) to h5, which is outside the current `--outline-tags h1,h2,h3,h4` range and therefore not in the PDF outline. That's a feature β€” the outline would be unreadable if every sub-page's Example / See Also / Remarks emitted a leaf. Class-index sections (`## Members` inside `AmbientProperties/index.md`) live at h4 and DO appear in the outline; if those become noisy too, narrow the flag to `h1,h2,h3`. +- **Foreword sits at outline depth 2 alongside chapter dividers.** The part-foreword article doesn't receive the extra heading shift, so its source H1 lands at H2 and reads as a peer of the chapter-divider H2s in the outline. Visually that's "Packages (foreword introduction) β†’ VBA Package β†’ VBRUN Package β†’ …" which matches the book structure β€” the foreword is its own thing within the part, not a chapter under another chapter. +- **Source authoring**: the VBA package's `/tB/Packages/VBA` landing page is special because its members keep their legacy `/tB/Modules/...` URLs. Other packages have their landing and members under one URL tree (`/tB/Packages//`). 1.9's `landing_page:` field covers both shapes β€” VBA points at `/tB/Packages/VBA` and lists `/tB/Modules/` as its prefix; VBRUN points at `/tB/Packages/VBRUN/` and lists the same as its prefix (the landing matches the prefix and the chapter loop filters it out to avoid double emission). + ## Phase 2 β€” In-PDF cross-references Goal: clicking "[SetData](SetData)" inside a "See Also" jumps to the SetData chapter in the PDF, not to `file://.../tB/Packages/VBRUN/DataObject/SetData.html`. @@ -518,6 +567,7 @@ Each phase is roughly 1-2 hours of work for me; ~1 working day end-to-end. Recom 1.6 sub-page nesting under index chapters. **Done.** 1.7 beyond Reference: front matter + supplementary parts. **Done.** (Welcome β†’ Introduction; Features, FAQ, Tutorials added before the Reference parts.) 1.8 catching up with the live nav: Reference Section + Packages move. **Done.** (New Reference Section Part; VBA Runtime gains the `/tB/Packages/VBA` landing page.) + 1.9 packages as chapters with full-page title pages. **Done.** (12 package Parts collapsed into one chaptered Packages Part; `/tB/Packages/` recast as foreword; landing H1s stripped by plugin; chapter body rendering factored into `_includes/book-chapter-body.html`. Follow-up fixes: chapter-divider H2 carries an explicit `chd-...-title` id so PDF outline entries jump to the divider rather than collapsing to page 1; chaptered-part chapters receive an `extra_heading_shift` so class / module indexes nest under the chapter divider in the outline rather than appearing as siblings.) 2. Phase 2 β€” cross-references. Largest navigation improvement. 2.1 permalink β†’ anchor map. **Done.** (Folded into the `_plugins/book-href-rewrite.rb` `Hash`.) 2.2 rewrite chapter-content href attributes. **Done.** (Plugin pass; ~50 ms over ~6900 rewrites.) diff --git a/docs/_data/book.yml b/docs/_data/book.yml index da7e651d..7f192c89 100644 --- a/docs/_data/book.yml +++ b/docs/_data/book.yml @@ -13,15 +13,43 @@ # on a numbered part. # # `parts` is an ordered list of numbered book parts. Each part has: -# title -- shown on the part divider page and in the global TOC. -# subtitle -- optional second-line descriptor on the divider. -# prefixes -- one or more URL prefixes; the book.html iterator gathers -# every page in `site.pages` whose URL starts with one of -# them, sorted by URL, and emits each as a chapter. -# page -- single absolute URL alternative to `prefixes`, used for -# one-chapter parts (e.g. the FAQ). Match is exact, not -# prefix-based, so a one-page section doesn't accidentally -# sweep in siblings. +# title -- shown on the part divider page and in the global TOC. +# subtitle -- optional second-line descriptor on the divider. +# prefixes -- one or more URL prefixes; book.html gathers every +# page in `site.pages` whose URL contains a prefix, +# sorts by URL, and emits each as a chapter. Used by +# "flat" parts that map directly to a folder of pages. +# page -- single absolute URL alternative to `prefixes`, used +# for one-chapter parts (e.g. the FAQ). Match is +# exact, not contains-based, so a one-page section +# doesn't accidentally sweep in siblings. +# foreword_page -- single absolute URL emitted as a `
` right after the part +# divider, before any chapter dividers. No running +# header on these pages (CSS suppresses chrome via a +# named page). Used by the Packages part to surface +# the `/tB/Packages/` landing as introductory +# material for the whole section. +# chapters -- ordered list of per-chapter entries (1.9). Each +# chapter has its own divider page and its own +# chapter-level content; the part becomes a +# container for several distinct subjects rather +# than a flat folder. Each entry has: +# title -- the chapter divider title. +# subtitle -- optional descriptor. +# landing_page -- the chapter's intro page; always +# emitted first within the +# chapter, and the plugin strips +# its source H1 so the chapter +# divider's H2 is the sole +# outline entry for the chapter. +# prefixes -- additional URL prefixes for +# content pages (members, etc.); +# sorted by URL after the +# landing. A page that matches +# a prefix AND is the landing is +# emitted only once (as the +# landing). # # Index pages (URL ending in `/`) sort before sibling content pages # under ASCII lexicographic order, so each part naturally opens with @@ -65,68 +93,89 @@ parts: - /tB/Controls - /tB/Gloss - - title: The VBA Runtime - subtitle: Standard runtime modules --- Strings, Math, FileSystem, and the rest - # The VBA package landing page lives at /tB/Packages/VBA (added when - # Packages was promoted to a top-level nav item in the live site), - # while individual module members keep their legacy /tB/Modules/... - # URLs. Both prefixes are listed so the landing page and the module - # contents end up in the same Part. - prefixes: - - /tB/Modules/ - - /tB/Packages/VBA - - - title: The VBRUN Package - subtitle: Runtime types for controls, errors, and the property bag - prefixes: - - /tB/Packages/VBRUN/ - - - title: The VB Package - subtitle: Classic VB6 forms and intrinsic controls - prefixes: - - /tB/Packages/VB/ - - - title: The WebView2 Package - subtitle: Chromium-based browser control via Microsoft Edge WebView2 - prefixes: - - /tB/Packages/WebView2/ - - - title: The Assert Package - subtitle: Test assertions with Exact, Strict, and Permissive comparison - prefixes: - - /tB/Packages/Assert/ - - - title: The CustomControls Package - subtitle: The WaynesControls suite and its supporting framework - prefixes: - - /tB/Packages/CustomControls/ - - - title: The CEF Package - subtitle: Chromium Embedded Framework browser control - prefixes: - - /tB/Packages/CEF/ - - - title: The WinEventLogLib Package - subtitle: Windows Event Log integration - prefixes: - - /tB/Packages/WinEventLogLib/ - - - title: The WinNamedPipesLib Package - subtitle: Asynchronous named-pipe framework over IOCP - prefixes: - - /tB/Packages/WinNamedPipesLib/ - - - title: The WinServicesLib Package - subtitle: Windows Services hosting - prefixes: - - /tB/Packages/WinServicesLib/ - - - title: The tbIDE Package - subtitle: IDE Extensibility --- the addin SDK - prefixes: - - /tB/Packages/tbIDE/ - - - title: The WinNativeCommonCtls Package - subtitle: VB6-compatible Common Controls replacement on top of Win32 ComCtl32 - prefixes: - - /tB/Packages/WinNativeCommonCtls/ + - title: Packages + subtitle: The runtime and library packages shipped with twinBASIC + # The /tB/Packages/ landing page is the part's foreword; its content + # (an overview of all built-in packages) sits between the part divider + # and the first chapter divider, with no running header. Each package + # below becomes a chapter with a dedicated chapter-divider title + # page; the chapter's own source landing page emits next (with its + # source H1 stripped by the plugin to avoid the redundancy with the + # chapter divider's H2), followed by the package's class / module / + # control pages in URL order. + foreword_page: /tB/Packages/ + chapters: + - title: VBA Package + subtitle: Standard runtime modules --- Strings, Math, FileSystem, and the rest + # The VBA landing lives at /tB/Packages/VBA (added when Packages + # was promoted to a top-level nav item), while individual module + # members keep their legacy /tB/Modules/... URLs. + landing_page: /tB/Packages/VBA + prefixes: + - /tB/Modules/ + + - title: VBRUN Package + subtitle: Runtime types for controls, errors, and the property bag + landing_page: /tB/Packages/VBRUN/ + prefixes: + - /tB/Packages/VBRUN/ + + - title: VB Package + subtitle: Classic VB6 forms and intrinsic controls + landing_page: /tB/Packages/VB/ + prefixes: + - /tB/Packages/VB/ + + - title: WebView2 Package + subtitle: Chromium-based browser control via Microsoft Edge WebView2 + landing_page: /tB/Packages/WebView2/ + prefixes: + - /tB/Packages/WebView2/ + + - title: Assert Package + subtitle: Test assertions with Exact, Strict, and Permissive comparison + landing_page: /tB/Packages/Assert/ + prefixes: + - /tB/Packages/Assert/ + + - title: CustomControls Package + subtitle: The WaynesControls suite and its supporting framework + landing_page: /tB/Packages/CustomControls/ + prefixes: + - /tB/Packages/CustomControls/ + + - title: CEF Package + subtitle: Chromium Embedded Framework browser control + landing_page: /tB/Packages/CEF/ + prefixes: + - /tB/Packages/CEF/ + + - title: WinEventLogLib Package + subtitle: Windows Event Log integration + landing_page: /tB/Packages/WinEventLogLib/ + prefixes: + - /tB/Packages/WinEventLogLib/ + + - title: WinNamedPipesLib Package + subtitle: Asynchronous named-pipe framework over IOCP + landing_page: /tB/Packages/WinNamedPipesLib/ + prefixes: + - /tB/Packages/WinNamedPipesLib/ + + - title: WinServicesLib Package + subtitle: Windows Services hosting + landing_page: /tB/Packages/WinServicesLib/ + prefixes: + - /tB/Packages/WinServicesLib/ + + - title: tbIDE Package + subtitle: IDE Extensibility --- the addin SDK + landing_page: /tB/Packages/tbIDE/ + prefixes: + - /tB/Packages/tbIDE/ + + - title: WinNativeCommonCtls Package + subtitle: VB6-compatible Common Controls replacement on top of Win32 ComCtl32 + landing_page: /tB/Packages/WinNativeCommonCtls/ + prefixes: + - /tB/Packages/WinNativeCommonCtls/ diff --git a/docs/_includes/book-chapter-body.html b/docs/_includes/book-chapter-body.html new file mode 100644 index 00000000..81d4a931 --- /dev/null +++ b/docs/_includes/book-chapter-body.html @@ -0,0 +1,267 @@ +{%- comment -%} + Per-chapter body processing for the book's chapter iteration. Pulled + out of book.html so the three call sites (front-matter loop, flat + per-part chapter loop, chaptered-part chapter loop) stay in sync. + + Inputs (passed via `{% include ... key=value %}`): + chapter page object (required). + article_class_override optional. When set, the emitted
+ uses this class literally; sub-page + detection still affects the internal + heading shift but doesn't add the + " sub-chapter" suffix. Default class is + "page" with " sub-chapter" appended for + sub-pages. + chapter_anchor_override optional. When set, replaces the URL- + derived chapter anchor. Used for the root + URL `/` in front matter where the default + derivation collapses to an empty seed; the + caller supplies an `fm.title`-derived + fallback. + skip_sub_page_detection optional truthy. When set, this chapter + doesn't update or read the + `current_index_*` state machine -- used by + front-matter (where there are no nested + sub-pages) so the root URL `/` doesn't + accidentally make every subsequent chapter + a sub-page of itself. + extra_heading_shift optional truthy. When set, applies an + additional +1 heading depth shift on top + of the standard 1.5a shift (and the 1.6b + sub-page shift, if applicable). Used by + chaptered-part chapters (1.9) so a class + index nests one outline level below its + chapter-divider H2 rather than appearing + as the chapter divider's sibling. + + Reads + mutates outer-scope globals: + current_index_url, current_index_kind, current_index_name -- the + 1.6a sub-page state machine. Caller is responsible for + initialising these before its for-loop and resetting between + independent chapter sequences (e.g. between chapters of a + chaptered Part). + p1_search/p1_replace .. p4_search/p4_replace, p3i*/p4i* -- the + whitespace patterns defined at the top of book.html. + + Emits one `
...
` block at the call site. Empty + chapter bodies are skipped silently (no article emitted). +{%- endcomment -%} +{%- assign first_char = include.chapter.content | strip | slice: 0, 1 -%} +{%- if first_char == '<' -%} + {%- assign body = include.chapter.content -%} +{%- else -%} + {%- assign body = include.chapter.content | markdownify -%} +{%- endif -%} + +{%- comment -%} + 1.6a sub-page detection + 1.6c kind/name capture. When we see an + index URL we read the index's H1 to decide class vs module -- + relying on the sub-page's `parent:` field would be wrong because + several VBRUN class folders mistakenly suffix it with " Module" + (e.g. AmbientProperties' BackColor.md). The H1 text (`# Foo class` + / `# Foo module`) is the source of truth. +{%- endcomment -%} +{%- assign is_sub_page = false -%} +{%- unless include.skip_sub_page_detection -%} + {%- assign url_last_char = include.chapter.url | slice: -1, 1 -%} + {%- if url_last_char == '/' -%} + {%- assign current_index_url = include.chapter.url -%} + {%- assign current_index_name = include.chapter.title + | replace: ' Module', '' + | replace: ' module', '' + | replace: ' Class', '' + | replace: ' class', '' + | replace: ' Package', '' -%} + {%- assign content_head = include.chapter.content | slice: 0, 200 | downcase -%} + {%- if content_head contains 'module' -%} + {%- assign current_index_kind = 'module' -%} + {%- else -%} + {%- assign current_index_kind = 'class' -%} + {%- endif -%} + {%- elsif current_index_url != '' -%} + {%- assign sized_prefix = include.chapter.url | slice: 0, current_index_url.size -%} + {%- if sized_prefix == current_index_url -%} + {%- assign is_sub_page = true -%} + {%- else -%} + {%- assign current_index_url = '' -%} + {%- endif -%} + {%- endif -%} +{%- endunless -%} + +{%- assign body = body + | replace: 'src="/', 'src="' + | replace: p1_search, p1_replace + | replace: p2_search, p2_replace + | replace: p3i12_search, p3i12_replace + | replace: p3i8_search, p3i8_replace + | replace: p3i4_search, p3i4_replace + | replace: p3_search, p3_replace + | replace: p4i16_search, p4i16_replace + | replace: p4i12_search, p4i12_replace + | replace: p4i8_search, p4i8_replace + | replace: p4i4_search, p4i4_replace + | replace: p4_search, p4_replace + | replace: ' ', '' + | replace: '', '

' + | replace: '', '' + | replace: '', '' + | replace: '', '' + | replace: '', '' -%} + + {%- comment -%} + 1.6b: extra heading shift for sub-pages so they nest one level + deeper than their parent index in the PDF outline. A sub-page's + chapter title (source `#`) ends up as `

` instead of `

`, + sub-page sections as `

` instead of `

`, and so on. The + shift cascades through the same rule set; no real content reaches + h7-stub or beyond after this second pass. + {%- endcomment -%} + {%- if is_sub_page -%} + {%- assign body = body + | replace: '', '' + | replace: '', '

' + | replace: '', '' + | replace: '', '' + | replace: '', '' -%} + {%- endif -%} + + {%- comment -%} + 1.9 extra shift: chaptered-part chapters nest under the chapter- + divider's H2, so every chapter body needs one more level of + demotion on top of 1.5a (and 1.6b, if the chapter is a sub-page). + Without this, a class index's source H1 lands at H2 -- the same + outline depth as the chapter divider it should sit beneath -- so + `AmbientProperties class` appears as a sibling of `VBRUN Package` + rather than a child. The shift cascades through the same rule set; + real content stops at source-H4 (member sub-page section), which + after 1.5a + 1.6b + this pass ends up at h7-stub and is excluded + from the outline anyway. + {%- endcomment -%} + {%- if include.extra_heading_shift -%} + {%- assign body = body + | replace: '', '' + | replace: '', '' + | replace: '', '' + | replace: '', '' + | replace: '', '' -%} + {%- endif -%} + + {%- comment -%} + 1.5b: chapter anchor + per-heading id uniqueness. Derive a stable + chapter anchor from the chapter URL (strip leading and trailing + slashes, replace inner slashes with dashes); the article element + carries the bare anchor as its id so Phase 2 cross-references can + target `#ch-` and land at the chapter's first page. Every + heading id and intra-chapter `href="#..."` inside the chapter body + gets that anchor prepended so identical kramdown-generated slugs + (`see-also`, `example`, ...) don't collapse to one outline + destination. + {%- endcomment -%} + {%- if include.chapter_anchor_override -%} + {%- assign chapter_anchor = include.chapter_anchor_override -%} + {%- else -%} + {%- assign url_path = include.chapter.url | replace: '/', '-' -%} + {%- assign anchor_first_char = url_path | slice: 0, 1 -%} + {%- if anchor_first_char == '-' -%} + {%- assign url_len = url_path.size | minus: 1 -%} + {%- assign url_path = url_path | slice: 1, url_len -%} + {%- endif -%} + {%- assign anchor_last_char = url_path | slice: -1, 1 -%} + {%- if anchor_last_char == '-' -%} + {%- assign url_len = url_path.size | minus: 1 -%} + {%- assign url_path = url_path | slice: 0, url_len -%} + {%- endif -%} + {%- assign chapter_anchor = 'ch-' | append: url_path -%} + {%- endif -%} + + {%- assign h2_class_prefix = '

+{{ header_title }} +{{ body }} +

+{%- endunless -%} diff --git a/docs/_plugins/book-href-rewrite.rb b/docs/_plugins/book-href-rewrite.rb index 0f13bb17..963ff872 100644 --- a/docs/_plugins/book-href-rewrite.rb +++ b/docs/_plugins/book-href-rewrite.rb @@ -27,9 +27,17 @@ # shape as `_plugins/offlinify.rb`. require "uri" +require "set" module BookHrefRewrite EXTERNAL_PREFIXES = ["http://", "https://", "mailto:", "#"].freeze + # Landing pages live inside a chaptered Part, where the include applies + # 1.5a + 1.9-extra-shift to every chapter body. The source H1 therefore + # arrives at the post-render HTML as an

, not an

. Stripping + # the first

keeps the chapter-divider's H2 as the chapter's sole + # outline entry at depth 2 -- without this strip the landing would emit + # a redundant H3 with the same title text, just one level deeper. + FIRST_LANDING_HEADING_REGEX = /]*>.*?<\/h3>/m.freeze # `url.gsub('/', '-').sub(/^-/, '').sub(/-$/, '')` then prepend "ch-". # Matches the chapter anchor scheme established in BOOKPLAN.md 1.5b. @@ -54,29 +62,68 @@ def self.parent_url_of(url) url.end_with?("/") ? url : url.sub(/[^\/]+\z/, "") end - # Iterates the book manifest's front-matter entries followed by its - # parts so both contribute to the chapter map. Each entry behaves - # the same way as far as URL gathering goes. + # Iterates the book manifest's front-matter entries, the parts' + # forewords (1.9), and the parts themselves -- both flat parts + # (`prefixes:`/`page:` directly on the part) and chaptered parts' + # individual chapter entries (`part.chapters` list, 1.9). Each + # yielded entry has the same shape as far as URL gathering goes: + # any combination of `page:`, `landing_page:`, and `prefixes:`. def self.book_entries(site) manifest = site.data["book"] return [] unless manifest - (manifest["front_matter"] || []) + (manifest["parts"] || []) + entries = [] + entries.concat(manifest["front_matter"] || []) + (manifest["parts"] || []).each do |part| + entries << part if part["page"] || part["prefixes"] + if part["foreword_page"] + entries << { "page" => part["foreword_page"], "title" => part["title"] } + end + (part["chapters"] || []).each { |ch| entries << ch } + end + entries end - # Pages matched by a single book.yml entry. Supports either - # `page:` (exact URL match, for one-chapter sections like the FAQ - # or the root index) or `prefixes:` (starts-with match per prefix, - # the original schema). + # Pages matched by a single book.yml entry. An entry may set any + # of `page:` (exact URL match, one-chapter sections like the FAQ + # or the root index), `landing_page:` (the chapter's intro page in + # a chaptered part; treated like `page:` for map-building), and + # `prefixes:` (starts-with match per prefix). The union is returned + # de-duplicated since the landing typically also matches one of the + # prefixes (e.g. `/tB/Packages/VBRUN/` landing matches the prefix + # `/tB/Packages/VBRUN/`). def self.entry_pages(entry, site) + pages = [] if entry["page"] - site.pages.select { |p| p.url == entry["page"] } - elsif entry["prefixes"] - entry["prefixes"].flat_map do |prefix| - site.pages.select { |p| p.url.start_with?(prefix) } + pages.concat(site.pages.select { |p| p.url == entry["page"] }) + end + if entry["landing_page"] + pages.concat(site.pages.select { |p| p.url == entry["landing_page"] }) + end + if entry["prefixes"] + entry["prefixes"].each do |prefix| + pages.concat(site.pages.select { |p| p.url.start_with?(prefix) }) + end + end + pages.uniq + end + + # Set of chapter anchors that correspond to a chaptered part's + # `landing_page:`. The plugin strips the first `

...

` (the + # source H1 after 1.5a + 1.9 extra shift) from these articles so the + # chapter-divider's H2 is the sole outline entry for the chapter at + # depth 2 -- without the strip the landing would emit a redundant H3 + # carrying the same title text one outline level deeper. + def self.build_landing_anchors(site) + set = Set.new + manifest = site.data["book"] + return set unless manifest + (manifest["parts"] || []).each do |part| + (part["chapters"] || []).each do |ch| + next unless ch["landing_page"] + set << chapter_anchor(ch["landing_page"], ch["title"]) end - else - [] end + set end # Build the permalink -> chapter-anchor map. Folder-style index @@ -179,14 +226,24 @@ def self.process(page) return if url_to_anchor.empty? parent_map = build_anchor_to_parent(site) return if parent_map.empty? + landing_anchors = build_landing_anchors(site) rewritten = 0 + landings_stripped = 0 page.output = page.output.gsub(/(]*id="(ch-[^"]+)"[^>]*>)(.*?)(<\/article>)/m) do article_open = Regexp.last_match(1) anchor_id = Regexp.last_match(2) body = Regexp.last_match(3) article_end = Regexp.last_match(4) + if landing_anchors.include?(anchor_id) + stripped_body = body.sub(FIRST_LANDING_HEADING_REGEX, "") + if stripped_body != body + body = stripped_body + landings_stripped += 1 + end + end + parent_url = parent_map[anchor_id] if parent_url new_body = rewrite_body(body, parent_url, url_to_anchor) @@ -196,7 +253,7 @@ def self.process(page) "#{article_open}#{body}#{article_end}" end - Jekyll.logger.info "BookHrefRewrite:", "rewrote #{rewritten} chapter bodies" + Jekyll.logger.info "BookHrefRewrite:", "rewrote #{rewritten} chapter bodies, stripped #{landings_stripped} landing H3s" end end diff --git a/docs/assets/css/print.css b/docs/assets/css/print.css index f5867a27..2c848294 100644 --- a/docs/assets/css/print.css +++ b/docs/assets/css/print.css @@ -176,6 +176,65 @@ article.front-matter { } +/* ---- Part foreword + chapter dividers (1.9) ----------------------------- + A chaptered Part (currently the Packages part) emits two new article + shapes in addition to its regular chapter bodies: + +
-- the part's intro page, sitting + between the part divider and + the first chapter divider. No + running header (named page). +
-- one per chapter, full-page + title page styled like a + scaled-down part divider. + + Chapter dividers use the same vertical centering as part dividers but + slightly smaller type, and no chapter number (the chapter ordinal is + implicit and doesn't read well at this position). The chapter title + on the divider is an H2 so it sits one level under the part divider's + H1 in the PDF outline; the corresponding source H1 on the chapter's + landing page is stripped by the book-href-rewrite plugin so the + outline shows the divider's H2 as the chapter's sole top-level entry. */ + +article.part-foreword { + page: part-foreword; + break-before: page; +} + +@page part-foreword { + @top-right { content: ""; } +} + +article.chapter-divider { + page: chapter-divider; + break-before: page; + text-align: center; + padding-top: 30%; +} + +article.chapter-divider h2 { + font-size: 24pt; + font-weight: 700; + border: none; + margin: 0 0 0.5em; + break-before: auto; + break-after: avoid; + line-height: 1.2; +} + +article.chapter-divider .chapter-subtitle { + font-size: 13pt; + font-style: italic; + color: #444; + margin: 0 auto; + max-width: 28em; +} + +@page chapter-divider { + @top-right { content: ""; } +} + + /* ---- Base typography ----------------------------------------------- */ html { @@ -251,6 +310,53 @@ article.page.sub-chapter > h3:first-of-type { margin-bottom: 0.7em; } +/* Chaptered-part headings (1.9). Chapters inside a chaptered part get + an additional +1 heading shift on top of 1.5a / 1.6b so they nest in + the PDF outline below their chapter-divider's H2 rather than as + siblings. The result: source-H1 lands at h3 (top-level chapter) or + h4 (sub-page), source-H2 lands at h4 or h5, and so on. Visual + styling mirrors the flat-part rules above but at one tag deeper. */ + +/* Top-level chapter in a chaptered part (source h1, now h3). */ +article.page.chaptered:not(.sub-chapter) > h3:first-of-type { + font-size: 24pt; + font-weight: 700; + border-bottom: none; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0.8em; +} + +/* Sub-page in a chaptered part (source h1, now h4). */ +article.page.chaptered.sub-chapter > h4:first-of-type { + font-size: 20pt; + font-weight: 700; + border-bottom: none; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0.7em; +} + +/* Section heading inside a chaptered-part chapter (source h2). */ +article.page.chaptered h4 { + font-size: 18pt; + font-weight: 700; + border-bottom: 0.5pt solid #bbb; + padding-bottom: 0.2em; +} + +/* Subsection heading inside a chaptered-part chapter (source h3) -- + Example / See Also / Remarks. */ +article.page.chaptered h5 { + font-size: 14pt; + font-weight: 600; + border: none; + padding: 0; +} + +article.page.chaptered h6 { font-size: 12pt; font-weight: 600; } +article.page.chaptered h7-stub { font-size: 11pt; font-weight: 600; color: #444; } + /* Generic h2 (only used outside chapters now, e.g. for any future front-matter sections). Kept for symmetry. */ h2 { diff --git a/docs/book.html b/docs/book.html index f561a19c..136bcaee 100644 --- a/docs/book.html +++ b/docs/book.html @@ -146,99 +146,16 @@

twinBASIC Documentation

{%- assign fm_chapters = "" | split: "" -%} {%- endif -%} {%- for chapter in fm_chapters -%} - {%- assign first_char = chapter.content | strip | slice: 0, 1 -%} - {%- if first_char == '<' -%} - {%- assign body = chapter.content -%} - {%- else -%} - {%- assign body = chapter.content | markdownify -%} - {%- endif -%} - {%- assign body = body - | replace: 'src="/', 'src="' - | replace: p1_search, p1_replace - | replace: p2_search, p2_replace - | replace: p3i12_search, p3i12_replace - | replace: p3i8_search, p3i8_replace - | replace: p3i4_search, p3i4_replace - | replace: p3_search, p3_replace - | replace: p4i16_search, p4i16_replace - | replace: p4i12_search, p4i12_replace - | replace: p4i8_search, p4i8_replace - | replace: p4i4_search, p4i4_replace - | replace: p4_search, p4_replace - | replace: ' ', '' - | replace: '', '

' - | replace: '', '' - | replace: '', '' - | replace: '', '' - | replace: '', '' -%} - - {%- comment -%} - Chapter anchor. For the root URL `/`, the default - slashes-to-dashes derivation collapses to an empty string; use - the front-matter entry's title (slugified) as the seed so the - anchor reads `ch-introduction` rather than just `ch-`. The - plugin uses the same fallback for the URL -> anchor map. - {%- endcomment -%} {%- if chapter.url == '/' -%} - {%- assign chapter_anchor = 'ch-' | append: fm.title | downcase | replace: ' ', '-' -%} + {%- assign fm_anchor = 'ch-' | append: fm.title | downcase | replace: ' ', '-' -%} {%- else -%} - {%- assign url_path = chapter.url | replace: '/', '-' -%} - {%- assign first_char = url_path | slice: 0, 1 -%} - {%- if first_char == '-' -%} - {%- assign url_len = url_path.size | minus: 1 -%} - {%- assign url_path = url_path | slice: 1, url_len -%} - {%- endif -%} - {%- assign last_char = url_path | slice: -1, 1 -%} - {%- if last_char == '-' -%} - {%- assign url_len = url_path.size | minus: 1 -%} - {%- assign url_path = url_path | slice: 0, url_len -%} - {%- endif -%} - {%- assign chapter_anchor = 'ch-' | append: url_path -%} + {%- assign fm_anchor = nil -%} {%- endif -%} - - {%- assign h2_class_prefix = '

-{{ body }} -

+ {%- include book-chapter-body.html + chapter=chapter + article_class_override='front-matter' + chapter_anchor_override=fm_anchor + skip_sub_page_detection=true -%} {%- endfor -%} {%- endfor -%} {%- endif -%} @@ -281,209 +198,98 @@

{{ part.title }}

name is enough -- when we hit a non-index URL we just check whether it starts with that prefix. {%- endcomment -%} - {%- assign current_index_url = '' -%} - {%- assign current_index_kind = 'class' -%} - {%- assign current_index_name = '' -%} - {%- for chapter in chapters -%} - {%- comment -%} - Jekyll's `chapter.content` returns rendered HTML for pages whose - Renderer#run has already converted them, but raw markdown for - pages whose conversion happens after book.html's. The state is a - function of Jekyll's internal rendering order and not stable - across builds. Detect by leading character and markdownify when - we still have raw source -- kramdown's `parse_block_html: true` - passes pre-rendered HTML through unchanged. - {%- endcomment -%} - {%- assign first_char = chapter.content | strip | slice: 0, 1 -%} - {%- if first_char == '<' -%} - {%- assign body = chapter.content -%} - {%- else -%} - {%- assign body = chapter.content | markdownify -%} - {%- endif -%} + {%- comment -%} + Foreword (1.9). When the part has `foreword_page:`, emit that page + as a `
` between the part divider + and the first chapter. No running header (CSS suppresses the + chrome via the `front-matter`-style named page). The foreword's + URL anchor is derived in the include the same way as any other + chapter so cross-references like `[Packages](/tB/Packages/)` + resolve naturally to `#ch-tB-Packages`. + {%- endcomment -%} + {%- if part.foreword_page -%} + {%- assign fw_chapters = site.pages | where: "url", part.foreword_page -%} + {%- for chapter in fw_chapters -%} + {%- include book-chapter-body.html + chapter=chapter + article_class_override='part-foreword' + skip_sub_page_detection=true -%} + {%- endfor -%} + {%- endif -%} + {%- if part.chapters -%} {%- comment -%} - 1.6a sub-page detection + 1.6c kind/name capture. When we see an - index URL we read the index's H1 to decide class vs module -- - relying on the sub-page's `parent:` field would be wrong because - several VBRUN class folders mistakenly suffix it with " Module" - (e.g. AmbientProperties' BackColor.md). The H1 text (`# Foo class` - / `# Foo module`) is the source of truth. + Chaptered part (1.9). Each chapter entry gets its own + `
` title page followed by the + chapter's pages (landing first, then prefix-matched content in + URL order). The sub-page state machine is reset between + chapters so each package's class / module folders nest only + against their own siblings. {%- endcomment -%} - {%- assign url_last_char = chapter.url | slice: -1, 1 -%} - {%- assign is_sub_page = false -%} - {%- if url_last_char == '/' -%} - {%- assign current_index_url = chapter.url -%} - {%- assign current_index_name = chapter.title - | replace: ' Module', '' - | replace: ' module', '' - | replace: ' Class', '' - | replace: ' class', '' - | replace: ' Package', '' -%} - {%- assign content_head = chapter.content | slice: 0, 200 | downcase -%} - {%- if content_head contains 'module' -%} - {%- assign current_index_kind = 'module' -%} + {%- for ch_entry in part.chapters -%} + {%- if ch_entry.landing_page -%} + {%- assign ch_url_path = ch_entry.landing_page | replace: '/', '-' -%} + {%- assign ch_first_char = ch_url_path | slice: 0, 1 -%} + {%- if ch_first_char == '-' -%} + {%- assign ch_url_len = ch_url_path.size | minus: 1 -%} + {%- assign ch_url_path = ch_url_path | slice: 1, ch_url_len -%} + {%- endif -%} + {%- assign ch_last_char = ch_url_path | slice: -1, 1 -%} + {%- if ch_last_char == '-' -%} + {%- assign ch_url_len = ch_url_path.size | minus: 1 -%} + {%- assign ch_url_path = ch_url_path | slice: 0, ch_url_len -%} + {%- endif -%} + {%- assign chapter_divider_id = 'chd-' | append: ch_url_path -%} {%- else -%} - {%- assign current_index_kind = 'class' -%} + {%- assign chapter_divider_id = 'chd-' | append: ch_entry.title | downcase | replace: ' ', '-' -%} {%- endif -%} - {%- elsif current_index_url != '' -%} - {%- assign sized_prefix = chapter.url | slice: 0, current_index_url.size -%} - {%- if sized_prefix == current_index_url -%} - {%- assign is_sub_page = true -%} - {%- else -%} - {%- assign current_index_url = '' -%} - {%- endif -%} - {%- endif -%} - {%- assign body = body - | replace: 'src="/', 'src="' - | replace: p1_search, p1_replace - | replace: p2_search, p2_replace - | replace: p3i12_search, p3i12_replace - | replace: p3i8_search, p3i8_replace - | replace: p3i4_search, p3i4_replace - | replace: p3_search, p3_replace - | replace: p4i16_search, p4i16_replace - | replace: p4i12_search, p4i12_replace - | replace: p4i8_search, p4i8_replace - | replace: p4i4_search, p4i4_replace - | replace: p4_search, p4_replace - | replace: ' ', '' - | replace: '', '' - | replace: '', '' - | replace: '', '' - | replace: '', '' - | replace: '', '' -%} - - {%- comment -%} - 1.6b: extra heading shift for sub-pages so they nest one level - deeper than their parent index in the PDF outline. A sub-page's - chapter title (source `#`) ends up as `

` instead of `

`, - sub-page sections as `

` instead of `

`, and so on. The - shift cascades through the same rule set; no real content reaches - h7-stub or beyond after this second pass. - {%- endcomment -%} - {%- if is_sub_page -%} - {%- assign body = body - | replace: '', '' - | replace: '', '

' - | replace: '', '' - | replace: '', '' - | replace: '', '' -%} - {%- endif -%} - - {%- comment -%} - 1.5b: chapter anchor + per-heading id uniqueness. Derive a - stable chapter anchor from the chapter URL (strip leading and - trailing slashes, replace inner slashes with dashes); the - article element carries the bare anchor as its id so Phase 2 - cross-references can target `#ch-` and land at the - chapter's first page. Every heading id and intra-chapter - `href="#..."` inside the chapter body gets that anchor - prepended so identical kramdown-generated slugs (`see-also`, - `example`, ...) don't collapse to one outline destination. - {%- endcomment -%} - {%- assign url_path = chapter.url | replace: '/', '-' -%} - {%- assign first_char = url_path | slice: 0, 1 -%} - {%- if first_char == '-' -%} - {%- assign url_len = url_path.size | minus: 1 -%} - {%- assign url_path = url_path | slice: 1, url_len -%} - {%- endif -%} - {%- assign last_char = url_path | slice: -1, 1 -%} - {%- if last_char == '-' -%} - {%- assign url_len = url_path.size | minus: 1 -%} - {%- assign url_path = url_path | slice: 0, url_len -%} - {%- endif -%} - {%- assign chapter_anchor = 'ch-' | append: url_path -%} - - {%- assign h2_class_prefix = '

` as its first child; CSS pins - `string-set: chapter-title` to that span (see print.css). Going - through a single dedicated element avoids pagedjs quirks with - `:first-of-type` + class-restricted selectors that left some - top-level chapter pages (Collection, Compilation, the package - landings in Parts 2+) with stale or empty running headers. +
+

{{ ch_entry.title }}

+{%- if ch_entry.subtitle %} +

{{ ch_entry.subtitle }}

+{%- endif %} +
- For sub-pages the span carries the compound title (1.6c); for - top-level chapters it carries just the chapter title. - {%- endcomment -%} - {%- if is_sub_page -%} - {%- if current_index_kind == 'module' -%} - {%- assign compound_sep = ' - ' -%} - {%- else -%} - {%- assign compound_sep = '.' -%} + {%- comment -%} + Build this chapter's page list: landing first (so it acts as + the chapter intro right after the divider), then prefix- + matched content sorted by URL with the landing filtered out + to avoid double emission when it also matches a prefix + (typical: /tB/Packages/Foo/ landing matches prefix + /tB/Packages/Foo/). + {%- endcomment -%} + {%- assign ch_chapters = "" | split: "" -%} + {%- if ch_entry.landing_page -%} + {%- assign ch_landing = site.pages | where: "url", ch_entry.landing_page -%} + {%- assign ch_chapters = ch_chapters | concat: ch_landing -%} {%- endif -%} - {%- assign header_title = current_index_name | append: compound_sep | append: chapter.title -%} -
-{{ header_title }} -{{ body }} -
- {%- else -%} - {%- assign header_title = chapter.title -%} -
-{{ header_title }} -{{ body }} -
- {%- endif -%} + {%- if ch_entry.prefixes -%} + {%- assign ch_rest = "" | split: "" -%} + {%- for prefix in ch_entry.prefixes -%} + {%- assign matched = site.pages | where_exp: "p", "p.url contains prefix" -%} + {%- assign ch_rest = ch_rest | concat: matched -%} + {%- endfor -%} + {%- assign ch_rest = ch_rest | sort: "url" -%} + {%- if ch_entry.landing_page -%} + {%- assign ch_landing_url = ch_entry.landing_page -%} + {%- assign ch_rest = ch_rest | where_exp: "p", "p.url != ch_landing_url" -%} + {%- endif -%} + {%- assign ch_chapters = ch_chapters | concat: ch_rest -%} + {%- endif -%} + + {%- assign current_index_url = '' -%} + {%- assign current_index_kind = 'class' -%} + {%- assign current_index_name = '' -%} + {%- for chapter in ch_chapters -%} + {%- include book-chapter-body.html chapter=chapter extra_heading_shift=true -%} + {%- endfor -%} {%- endfor -%} + {%- else -%} + {%- assign current_index_url = '' -%} + {%- assign current_index_kind = 'class' -%} + {%- assign current_index_name = '' -%} + {%- for chapter in chapters -%} + {%- include book-chapter-body.html chapter=chapter -%} + {%- endfor -%} + {%- endif -%} {%- endfor %} From 8087a45a1c22cecbf4686bf7e57b1b38f5b4d6a8 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sun, 17 May 2026 00:05:02 +0200 Subject: [PATCH 25/27] Fix page numbering resets at the start of each part. --- docs/assets/css/print.css | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/assets/css/print.css b/docs/assets/css/print.css index 2c848294..929f5eb5 100644 --- a/docs/assets/css/print.css +++ b/docs/assets/css/print.css @@ -114,6 +114,16 @@ article.part-divider { page: divider; text-align: center; padding-top: 35%; + /* Restart page numbering at each Part so each section of the book reads + like a self-contained volume. Pagedjs's Counters module picks this up + and emits a `data-counter-page-reset` attribute on the element; the + `afterPageLayout` hook then injects a per-page rule that resets the + `page` counter on the part divider's page. Reset to 0 (not 1) so the + first content page after the divider reads "1" once the auto-increment + fires. The divider's @page rule suppresses both the running header and + the page counter (see @page divider below) so the divider page itself + reads as unnumbered, matching traditional book convention. */ + counter-reset: page 0; } article.part-divider .part-number { @@ -151,7 +161,8 @@ article.part-divider .part-intro { } @page divider { - @top-right { content: ""; } + @top-right { content: ""; } + @bottom-right { content: ""; } } From 88e37350577f30aed3101a24de97c010c6c504c4 Mon Sep 17 00:00:00 2001 From: Kuba Sunderland-Ober Date: Sun, 17 May 2026 15:31:25 +0200 Subject: [PATCH 26/27] Add the _site-pdf build into the primary build. --- BOOKPLAN.md | 17 +-- WIP.md | 4 +- docs/.gitignore | 1 - .../Documentation Development.md | 6 +- docs/_config-pdf.yml | 25 --- docs/_config.yml | 10 ++ docs/_config_offline.yml | 27 ---- docs/_layouts/book.html | 14 -- docs/_plugins/offlinify.md | 38 ++--- docs/_plugins/offlinify.rb | 124 ++++++--------- docs/_plugins/pdfify.md | 119 +++++++++++++++ docs/_plugins/pdfify.rb | 143 ++++++++++++++++++ docs/book.bat | 8 +- docs/build-offline.bat | 1 - docs/build.bat | 2 +- 15 files changed, 351 insertions(+), 188 deletions(-) delete mode 100644 docs/_config-pdf.yml delete mode 100644 docs/_config_offline.yml delete mode 100644 docs/_layouts/book.html create mode 100644 docs/_plugins/pdfify.md create mode 100644 docs/_plugins/pdfify.rb delete mode 100644 docs/build-offline.bat diff --git a/BOOKPLAN.md b/BOOKPLAN.md index ff9eb3e4..74151adf 100644 --- a/BOOKPLAN.md +++ b/BOOKPLAN.md @@ -9,28 +9,27 @@ This file is the staged plan for turning that output into an actual book. Phases From `docs/`: ``` -bundle exec jekyll build --config _config.yml,_config-pdf.yml -npx pagedjs-cli _site-pdf/book.html -o _pdf/book.pdf --outline-tags h1,h2,h3 -t 600000 +bundle exec jekyll build # produces _site/, _site-offline/, _site-pdf/ +npx pagedjs-cli _site-pdf/book.html -o _pdf/book.pdf --outline-tags h1,h2,h3,h4 -t 600000 ``` -or `book.bat`. Render is jekyll (~5 s) then pagedjs-cli (~2 min) for the full book; iterate CSS by refreshing `_site-pdf/book.html` directly in a browser (no pagedjs needed) and only re-run pagedjs to confirm pagination. +or `build.bat` then `book.bat`. One Jekyll invocation produces three trees in parallel: `_site/` is the online site, `_site-offline/` is the file://-browsable mirror produced by `_plugins/offlinify.rb`, and `_site-pdf/` is the sparse pagedjs source produced by `_plugins/pdfify.rb` β€” just `book.html`, the two stylesheets the book layout links, and the images `book.html` references. Render time: jekyll ~24 s (was ~7 s before sub-page nesting et al., the heavy work is now in plugins), then pagedjs-cli ~2 min for the full book. Iterate CSS by refreshing `_site-pdf/book.html` directly in a browser (no pagedjs needed) and only re-run pagedjs to confirm pagination. Touch points and what each one already exposes: - [docs/book.html](docs/book.html) β€” iterator that concatenates every chapter into one HTML document. Permalink `/book.html`, layout `book-combined`. Contains: whitespace-pattern primitives (p1..p4, indent variants) for the pagedjs whitespace fix; the Roman numerals array; the title-page section (1.3); the front-matter loop (1.7) that emits `site.data.book.front_matter` entries inline between the title page and Part I; the per-part loop. Each part can be **flat** (a single `chapters` list gathered from `page:` / `prefixes:`) or **chaptered** (1.9; a `foreword_page:` plus a nested `chapters:` list, each with its own divider page). Per-chapter body rendering β€” whitespace fix, heading depth shift, sub-page detection, id uniqueness, header-string emission β€” is factored out into `_includes/book-chapter-body.html` so all three call sites (front-matter, flat part, chaptered part) share one implementation. Insertion points for new front matter go **after** the title-page section and **before** the `{%- for part in site.data.book.parts -%}` opener. - [docs/_includes/book-chapter-body.html](docs/_includes/book-chapter-body.html) β€” per-chapter body processing, called via `{% include book-chapter-body.html chapter=... %}` from each of book.html's chapter-loop callers. Handles markdownify, the pagedjs whitespace fix (consumes the `p1..p4` patterns from the outer scope), heading depth shift (1.5a + sub-page 1.6b), sub-page detection (1.6a, opt-out via `skip_sub_page_detection`), heading-id uniqueness (1.5b), compound running header (1.6c), and emits the final `
` block. Take-it-or-leave-it parameters cover the cases that don't fit the default: `article_class_override` (front-matter and part-foreword), `chapter_anchor_override` (root URL `/` fallback to `ch-introduction`), `skip_sub_page_detection` (front-matter entries don't share an index hierarchy with following chapters). -- [docs/_layouts/book-combined.html](docs/_layouts/book-combined.html) β€” minimal wrapper: `` + `{{ site.title }}` + `rouge.css` + `print.css` + `{{ content }}`. No nav, no JS, no chrome. Pagedjs runs on the rendered output of this layout. -- [docs/_layouts/book.html](docs/_layouts/book.html) β€” per-source-page wrapper used when each `Reference/...` page is rendered to its own `_site-pdf/.html`. Not part of the PDF render path itself; the combined `book.html` iterates `site.pages` to gather these as chapters. +- [docs/_layouts/book-combined.html](docs/_layouts/book-combined.html) β€” minimal wrapper: `` + `{{ site.title }}` + `rouge.css` + `print.css` + `{{ content }}`. No nav, no JS, no chrome. Pagedjs runs on the rendered output of this layout. The only layout the PDF pipeline uses; the older per-source-page `book` layout was retired alongside `_config-pdf.yml`. - [docs/assets/css/print.css](docs/assets/css/print.css) β€” the book's design. Existing structural rules: `@page` (A4, 22mm margins, running header in `@top-right` via `string(chapter-title)`, page number in `@bottom-right`); `@page :first` (suppresses both β€” used by the title page); `@page divider` (suppresses both, used by part dividers via `page: divider`); `@page front-matter` (suppresses both, used by `article.front-matter` for 1.7 Introduction-style sections); `@page part-foreword` + `@page chapter-divider` (suppresses both, used by the 1.9 part foreword and per-chapter title pages); `article { break-before: page }`; per-chapter `string-set: chapter-title` on `article.page > .header-string`; the top-level vs sub-chapter heading-size split (`article.page:not(.sub-chapter) > h2:first-of-type` vs `article.page.sub-chapter > h3:first-of-type`); chapter-divider H2 typography (`article.chapter-divider h2` β€” 24pt centered, no border) plus its subtitle (`.chapter-subtitle` β€” 13pt italic). - [docs/_data/book.yml](docs/_data/book.yml) β€” the manifest book.html iterates over. Schema: `parts:` is an ordered list of numbered parts. A flat part has `{ title, subtitle, prefixes }` or `{ title, subtitle, page }`. A **chaptered** part (1.9) has `{ title, subtitle, foreword_page, chapters }`, where `chapters` is an ordered list of per-chapter entries `{ title, subtitle, landing_page, prefixes }`; each chapter emits a full-page `
` title page followed by its landing page (with the source H1 stripped by the plugin) and the prefix-matched content in URL order. `front_matter:` is a sibling list of front-matter sections (1.7), same per-entry shape as a flat part. `prefixes:` is a list of URL substrings matched via `contains` (multiple prefixes can map to one Part / chapter β€” used for the Reference Section in 1.8 and for the VBA chapter's landing at `/tB/Packages/VBA` + members under `/tB/Modules/...`); `page:` is a single exact-URL match. Available in Liquid as `site.data.book.parts` and `site.data.book.front_matter`. - [docs/_data/build.yml](#) β€” **not committed**. Build provenance lives in `site.data.build` (populated in memory by the plugin), so the YAML file is never written. The fields exposed are `site.data.build.commit` (short hash) and `site.data.build.commit_date` (ISO date, `%cs`), or `'unknown'` when git is unavailable. -- [docs/_config.yml](docs/_config.yml) β€” the regular-site config. Two fields the book reads: `site.title` ("twinBASIC Documentation") and `site.footer_content` (the canonical copyright string, reused by the title page and colophon). -- [docs/_config-pdf.yml](docs/_config-pdf.yml) β€” overlay config layered on top of `_config.yml`. Switches `defaults: layout: book` and `destination: _site-pdf`. The full `defaults:` block has to be restated (Jekyll replaces top-level array keys; it does not merge them). +- [docs/_config.yml](docs/_config.yml) β€” the regular-site config. Reads `site.title` ("twinBASIC Documentation") and `site.footer_content` (the canonical copyright string, reused by the title page and colophon). Also exposes the two combined-build toggles the post-write plugins consult: `also_build_offline: true` (offlinify) and `also_build_pdf: true` (pdfify). Both default to true in the committed config; flip either to `false` to skip that output without touching `_site/`. - [docs/_plugins/build-info.rb](docs/_plugins/build-info.rb) β€” captures `git rev-parse --short HEAD` and `git log -1 --format=%cs` into `site.data['build']` on `:site, :post_read`. Falls back to `'unknown'` placeholders when git isn't on PATH. - [docs/_plugins/build-phase-timing.rb](docs/_plugins/build-phase-timing.rb) β€” the cleanest hook-pattern example to copy when writing a new `_plugins/` file (uses every `:site, :hook` boundary). -- [docs/_plugins/offlinify.rb](docs/_plugins/offlinify.rb) β€” the offline-site link rewriter; reference example for build-time concerns tightly coupled to Jekyll's URL model. +- [docs/_plugins/offlinify.rb](docs/_plugins/offlinify.rb) β€” the offline-site link rewriter; reference example for build-time concerns tightly coupled to Jekyll's URL model. Runs on `:site, :post_write` when `also_build_offline: true`. +- [docs/_plugins/pdfify.rb](docs/_plugins/pdfify.rb) β€” emits the sparse `_site-pdf/` tree that pagedjs-cli consumes. Reads `/book.html`, copies it verbatim alongside `assets/css/print.css` + `assets/css/rouge.css` + every relative `` target into `-pdf/`. Runs on `:site, :post_write` when `also_build_pdf: true`; retires the older `_config-pdf.yml` second-Jekyll-pass approach. - [docs/_plugins/book-href-rewrite.rb](docs/_plugins/book-href-rewrite.rb) β€” post-render Ruby pass for Phase 2.2 cross-references **plus** the 1.9 landing-page H1 strip. Walks each `
` chapter body in the rendered `book.html`, resolves relative-path hrefs against the chapter's URL parent via `URI.merge` (RFC-3986 path normalization from the standard library β€” no manual `../` folding), and rewrites in-book absolute URLs to `#ch-...` anchors using a `Hash` map built from `_data/book.yml` + `site.pages`. The manifest iteration (`book_entries`) covers `front_matter:`, each part's optional `foreword_page:`, flat parts directly, and each chapter inside a chaptered part. The URL β†’ anchor map symmetrizes the trailing-slash form for folder-style indexes **and** the `.html` suffix. For articles whose anchor matches a chapter `landing_page:`, the plugin strips the first `

...

` from the body (after Liquid's heading shift) so the chapter-divider's H2 is the chapter's sole outline entry. Out-of-book hrefs emit in their resolved absolute form so they're greppable as `href="/..."` during verification. Hooked into `:pages, :post_render` and filtered to `page.path == "book.html"`; non-book pages incur no cost. Replaces an earlier in-template Liquid implementation (~21 s of render overhead vs ~50 ms here). -- [docs/book.bat](docs/book.bat) β€” invokes `bundle exec jekyll build --config _config.yml,_config-pdf.yml || exit /b` then `npx pagedjs-cli _site-pdf\book.html -o _pdf\book.pdf --outline-tags h1,h2,h3,h4 -t 600000`. Must be run from `cmd.exe`, not PowerShell (see gotchas). +- [docs/book.bat](docs/book.bat) β€” now only the pagedjs render step: checks `_site-pdf\book.html` exists, makes `_pdf\`, then `npx pagedjs-cli _site-pdf\book.html -o _pdf\book.pdf --outline-tags h1,h2,h3,h4 -t 600000`. Run `build.bat` first (or `bundle exec jekyll build`) to populate `_site-pdf/`. Must be run from `cmd.exe`, not PowerShell (see gotchas). ## Build-time tooling policy diff --git a/WIP.md b/WIP.md index 358391b5..8d436a94 100644 --- a/WIP.md +++ b/WIP.md @@ -428,10 +428,10 @@ Python scripts are reserved for non-render concerns: one-off content conversion From `docs/`: -- `bundle exec jekyll build` (or `build.bat`) β€” builds the online copy to `_site/` **and** a `file://`-browsable copy to `_site-offline/` in a single Jekyll run. The offline pass adds ~3-5s on top of the normal ~13s build; activated by `also_build_offline: true` in `_config.yml`. After Jekyll's WRITE phase, `_plugins/offlinify.rb` walks `_site/`, copies binary assets verbatim into `_site-offline/`, and for each HTML and CSS file rewrites every root-absolute `href` / `src` / `url()` to a page-relative path with the resolved file extension (`/FAQ` β†’ `../../FAQ.html`, `/Tutorials/CEF/` β†’ `../../Tutorials/CEF/index.html`). It also patches the offline copy of `assets/js/just-the-docs.js` in two places β€” `navLink()` to match the active nav entry by resolved DOM `link.href` rather than `document.location.pathname` (the upstream pathname-vs-attribute compare returns no match under `file://`, leaving the sidebar with no `.active` class so the nav appears collapsed on every navigation), and `initSearch()` to read the lunr index from `window.SEARCH_DATA` rather than fetching `search-data.json` over `XMLHttpRequest` (XHR to `file://` resources is blocked by browsers; classic `