diff --git a/README.md b/README.md index 185a540..4ee617a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ npm run dev This starts a local server at `http://localhost:8090` with live SCSS recompilation. JavaScript source files run directly. refresh after editing them. Build deployable assets with `npm run build`. output is written to `build/`. +The build inlines the small stylesheet and early theme script to avoid blocking +requests, and adds content hashes to asset URLs for safe browser caching. +The website Caddy configuration compresses text responses and caches versioned +assets for one year. HTML and unversioned assets revalidate on each visit. ## Design and assets @@ -29,10 +33,11 @@ Without JavaScript, the system theme, navigation, screenshot and download links remain usable. Reduced-motion preferences disable smooth scrolling. Brand SVGs in `public/img/` are copied from the client's `public/img/` assets. -The library screenshot is the original client README image, with local WebP -variants at 640, 1280, 1920 and 2560 pixels. The full-resolution PNG is available -through the enlargement link and is the social preview image. It always shows -the real dark-mode app, independent of the website theme. Favicons use the +The library screenshots show the real app in light and dark modes, with local +WebP variants at 640, 768, 1280, 1920 and 2560 pixels. The screenshot follows the +website theme. Clicking it opens an on-page preview of the full-resolution JPEG, +which can be closed by tapping the image, pressing Escape, or clicking outside it. +The light screenshot is the social preview image. Favicons use the canonical emblem from the client's `app/assets/images/logo-icon-light.svg`. These are checked-in assets. website builds do not need the client repository or image-generation tools. diff --git a/deploy/Caddyfile b/deploy/Caddyfile index 266eff5..a8b2792 100644 --- a/deploy/Caddyfile +++ b/deploy/Caddyfile @@ -1,4 +1,6 @@ :8080 { + encode zstd gzip + @status path /status.json handle @status { root * /srv/status @@ -7,6 +9,12 @@ } handle { root * /srv/site/current + @versioned { + path /css/* /js/* /public/* + query v=* + } + header @versioned Cache-Control "public, max-age=31536000, immutable" + header ?Cache-Control "no-cache" file_server } } diff --git a/index.html b/index.html index 305aa3d..d6c9735 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,7 @@ content="Organize physical and digital books with Papyrus, a free, open-source e-book reader and library manager. Read offline or sync across devices."> - + @@ -43,7 +43,7 @@ "operatingSystem": "Windows, Linux", "browserRequirements": "A modern web browser for the web app.", "downloadUrl": "https://github.com/PapyrusReader/client/releases/latest", - "screenshot": "https://papyrus-reader.com/public/img/library.png", + "screenshot": "https://papyrus-reader.com/public/img/library-light.jpg", "license": "https://github.com/PapyrusReader/client/blob/master/LICENSE", "isAccessibleForFree": true, "offers": { "@type": "Offer", "price": 0 }, @@ -93,8 +93,8 @@
-

Your books, your data,
every device.

-

Papyrus is a free, open-source app for organizing physical and digital books, reading e-books, and syncing your library across devices.

+

Your books, your data,
every device.

+

Papyrus is a free, open-source app that brings your physical and digital books into one library. Access your books across devices, track your reading habits, and more.

Try in browser @@ -102,17 +102,23 @@

Your books, your data,
every device.

- - Papyrus in dark mode, with physical books and ebooks displayed together in a cover grid + + + + Papyrus library with physical books and ebooks displayed together in a cover grid + -
View full size
+ + Papyrus library screenshot +

Features

diff --git a/js/main.js b/js/main.js index 6beba03..2e62f9c 100644 --- a/js/main.js +++ b/js/main.js @@ -25,8 +25,32 @@ if (!links.contains(event.target) && !menuButton.contains(event.target)) closeMenu(); }); window.matchMedia('(max-width: 720px)').addEventListener('change', closeMenu); - const updateThemeButton = () => { - const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; + const libraryPreview = document.querySelector('.library-preview'); + const libraryDialog = document.getElementById('library-dialog'); + const updatePreviewImage = () => { + const image = libraryDialog.querySelector('img'); + image.src = libraryPreview.querySelector('[data-library-full-size]').href; + image.alt = libraryPreview.querySelector('img').alt; + }; + if (libraryPreview && libraryDialog) { + libraryPreview.querySelector('[data-library-full-size]').addEventListener('click', (event) => { + event.preventDefault(); + updatePreviewImage(); + libraryDialog.showModal(); + }); + libraryDialog.addEventListener('click', () => libraryDialog.close()); + } + const updateThemeUI = () => { + const theme = document.documentElement.dataset.theme; + const next = theme === 'dark' ? 'light' : 'dark'; + if (libraryPreview) { + libraryPreview.querySelector('[data-library-dark]').media = theme === 'dark' ? 'all' : 'not all'; + libraryPreview.querySelector('img').alt = `Papyrus in ${theme} mode, with physical books and ebooks displayed together in a cover grid`; + libraryPreview.querySelectorAll('[data-library-full-size]').forEach((link) => { + link.href = `public/img/library-${theme}.jpg`; + }); + if (libraryDialog && libraryDialog.open) updatePreviewImage(); + } themeLabel.textContent = next === 'dark' ? 'Dark' : 'Light'; themeButton.querySelector('svg').innerHTML = next === 'dark' ? '' @@ -35,8 +59,8 @@ }; if (window.PapyrusTheme) { themeButton.hidden = false; - updateThemeButton(); + updateThemeUI(); themeButton.addEventListener('click', () => window.PapyrusTheme.toggle()); - window.addEventListener('papyrus:themechange', updateThemeButton); + window.addEventListener('papyrus:themechange', updateThemeUI); } })(); diff --git a/js/tests/build.test.cjs b/js/tests/build.test.cjs new file mode 100644 index 0000000..3f4635e --- /dev/null +++ b/js/tests/build.test.cjs @@ -0,0 +1,44 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const os = require('node:os'); +const path = require('node:path'); +const { execFileSync } = require('node:child_process'); + +test('built pages inline critical assets and invalidate cached URLs when asset contents change', () => { + const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'papyrus-build-test-')); + const write = (name, contents) => fs.writeFileSync(path.join(directory, name), contents); + const read = (name) => fs.readFileSync(path.join(directory, name), 'utf8'); + try { + for (const name of ['build/css', 'build/js', 'build/status', 'public/img']) { + fs.mkdirSync(path.join(directory, name), { recursive: true }); + } + write('build/css/style.css', 'body{color:red}'); + write('build/js/theme.js', 'window.theme = "$&";'); + write('build/js/main.js', 'window.ready = true;'); + write('public/img/book.webp', 'first image'); + const build = () => { + for (const [file, prefix] of [['index.html', ''], ['status/index.html', '/']]) { + write(`build/${file}`, ``); + } + execFileSync(process.execPath, [path.resolve(__dirname, '../../scripts/stamp-version.js')], { cwd: directory }); + }; + build(); + const before = read('build/index.html'); + for (const file of ['build/index.html', 'build/status/index.html']) { + const html = read(file); + assert.ok(html.includes('')); + assert.ok(html.includes('')); + assert.doesNotMatch(html, /src="[^"]*theme\.js|rel="stylesheet"/); + assert.match(html, /book\.webp\?v=[a-f0-9]{16} 768w/); + assert.match(html, /main\.js\?v=[a-f0-9]{16}" defer/); + } + write('public/img/book.webp', 'replacement image'); + build(); + const after = read('build/index.html'); + assert.notEqual(before.match(/book\.webp\?v=\w+/)[0], after.match(/book\.webp\?v=\w+/)[0]); + assert.equal(before.match(/main\.js\?v=\w+/)[0], after.match(/main\.js\?v=\w+/)[0]); + } finally { + fs.rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/package-lock.json b/package-lock.json index 7860394..080f3ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,14 @@ }, "devDependencies": { "concurrently": "^9.0.0", - "esbuild": "^0.24.0", + "esbuild": "^0.28.2", "sass": "^1.80.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -34,9 +34,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -51,9 +51,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -68,9 +68,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -85,9 +85,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -102,9 +102,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -119,9 +119,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -136,9 +136,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -153,9 +153,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -170,9 +170,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -187,9 +187,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -204,9 +204,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -221,9 +221,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -238,9 +238,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -255,9 +255,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -272,9 +272,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -289,9 +289,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -306,9 +306,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -323,9 +323,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -340,9 +340,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -357,9 +357,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -373,10 +373,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -391,9 +408,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -408,9 +425,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -425,9 +442,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -758,15 +775,15 @@ "license": "MIT" }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -952,9 +969,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1116,14 +1133,15 @@ } }, "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.2.tgz", + "integrity": "sha512-o8vI5RE5A6EVVOd9o41jKp41aJom+QTEO/Bx8MYNjexMo/Bv2WOjUfZr+aL0WnYSgymUy6zeguqLTsIhV0gMvQ==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", + "destroy": "1.2.0", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", @@ -1131,6 +1149,10 @@ }, "engines": { "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/concat-map": { @@ -1140,15 +1162,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.5", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.5.tgz", + "integrity": "sha512-YMZlvWrTPQEhjKp60XSmMGt60+pffbv6DyRnOMvVO98+J7aF8w8Ui9FhdcOwvSixc/kyR6SoVvmejcn/2lZBqg==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.12.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -1205,6 +1227,16 @@ "node": ">=4.0.0" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1229,9 +1261,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1242,31 +1274,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escalade": { @@ -1308,6 +1341,22 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1349,9 +1398,9 @@ } }, "node_modules/immutable": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", - "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, "license": "MIT" }, @@ -1504,9 +1553,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1605,9 +1654,9 @@ "license": "MIT" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "optional": true, @@ -1618,15 +1667,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -1758,13 +1798,13 @@ } }, "node_modules/serve": { - "version": "14.2.5", - "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", - "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.6.tgz", + "integrity": "sha512-QEjUSA+sD4Rotm1znR8s50YqA3kYpRGPmtd5GlFxbaL9n/FdUNbqMhxClqdditSk0LlZyA/dhud6XNRTOC9x2Q==", "license": "MIT", "dependencies": { "@zeit/schemas": "2.36.0", - "ajv": "8.12.0", + "ajv": "8.18.0", "arg": "5.0.2", "boxen": "7.0.0", "chalk": "5.0.1", @@ -1772,7 +1812,7 @@ "clipboardy": "3.0.0", "compression": "1.8.1", "is-port-reachable": "4.0.0", - "serve-handler": "6.1.6", + "serve-handler": "6.1.7", "update-check": "1.5.4" }, "bin": { @@ -1783,15 +1823,15 @@ } }, "node_modules/serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", "license": "MIT", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", - "minimatch": "3.1.2", + "minimatch": "3.1.5", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" @@ -1840,9 +1880,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", "dev": true, "license": "MIT", "engines": { @@ -1859,9 +1899,9 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1967,15 +2007,6 @@ "registry-url": "3.1.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 788f388..72f47a0 100644 --- a/package.json +++ b/package.json @@ -8,17 +8,22 @@ "build:html": "cp index.html build/ && cp -r status build/ && node scripts/stamp-version.js", "build:js": "esbuild js/theme.js js/main.js js/services.js js/downloads.js js/status.js --minify --outdir=build/js", "build:assets": "cp -r public build/ && cp robots.txt sitemap.xml build/", - "build": "npm run clean && mkdir -p build/css build/js build/public && npm run build:scss && npm run build:html && npm run build:js && npm run build:assets", + "build": "npm run clean && mkdir -p build/css build/js build/public && npm run build:scss && npm run build:js && npm run build:html && npm run build:assets", "watch:scss": "sass scss/style.scss css/style.css --style=expanded --no-source-map --watch", "dev": "concurrently -n scss,server -c magenta,green \"npm run watch:scss\" \"serve -l 8090\"", "test": "node --test js/tests/*.test.cjs" }, "devDependencies": { "concurrently": "^9.0.0", - "esbuild": "^0.24.0", + "esbuild": "^0.28.2", "sass": "^1.80.0" }, "dependencies": { "serve": "^14.2.5" + }, + "overrides": { + "serve": { + "compression": "^1.8.2" + } } } diff --git a/public/img/library-1280.webp b/public/img/library-1280.webp deleted file mode 100644 index cf2e799..0000000 Binary files a/public/img/library-1280.webp and /dev/null differ diff --git a/public/img/library-1920.webp b/public/img/library-1920.webp deleted file mode 100644 index d4e6380..0000000 Binary files a/public/img/library-1920.webp and /dev/null differ diff --git a/public/img/library-2560.webp b/public/img/library-2560.webp deleted file mode 100644 index d432c2f..0000000 Binary files a/public/img/library-2560.webp and /dev/null differ diff --git a/public/img/library-640.webp b/public/img/library-640.webp deleted file mode 100644 index c585e95..0000000 Binary files a/public/img/library-640.webp and /dev/null differ diff --git a/public/img/library-dark-1280.webp b/public/img/library-dark-1280.webp new file mode 100644 index 0000000..7af01ee Binary files /dev/null and b/public/img/library-dark-1280.webp differ diff --git a/public/img/library-dark-1920.webp b/public/img/library-dark-1920.webp new file mode 100644 index 0000000..8e2b270 Binary files /dev/null and b/public/img/library-dark-1920.webp differ diff --git a/public/img/library-dark-2560.webp b/public/img/library-dark-2560.webp new file mode 100644 index 0000000..77b89bd Binary files /dev/null and b/public/img/library-dark-2560.webp differ diff --git a/public/img/library-dark-640.webp b/public/img/library-dark-640.webp new file mode 100644 index 0000000..42aeea9 Binary files /dev/null and b/public/img/library-dark-640.webp differ diff --git a/public/img/library-dark-768.webp b/public/img/library-dark-768.webp new file mode 100644 index 0000000..7e8c226 Binary files /dev/null and b/public/img/library-dark-768.webp differ diff --git a/public/img/library-dark.jpg b/public/img/library-dark.jpg new file mode 100644 index 0000000..645e6d6 Binary files /dev/null and b/public/img/library-dark.jpg differ diff --git a/public/img/library-light-1280.webp b/public/img/library-light-1280.webp new file mode 100644 index 0000000..fe9670f Binary files /dev/null and b/public/img/library-light-1280.webp differ diff --git a/public/img/library-light-1920.webp b/public/img/library-light-1920.webp new file mode 100644 index 0000000..e40b781 Binary files /dev/null and b/public/img/library-light-1920.webp differ diff --git a/public/img/library-light-2560.webp b/public/img/library-light-2560.webp new file mode 100644 index 0000000..628a946 Binary files /dev/null and b/public/img/library-light-2560.webp differ diff --git a/public/img/library-light-640.webp b/public/img/library-light-640.webp new file mode 100644 index 0000000..71df14e Binary files /dev/null and b/public/img/library-light-640.webp differ diff --git a/public/img/library-light-768.webp b/public/img/library-light-768.webp new file mode 100644 index 0000000..c641526 Binary files /dev/null and b/public/img/library-light-768.webp differ diff --git a/public/img/library-light.jpg b/public/img/library-light.jpg new file mode 100644 index 0000000..5b8d00e Binary files /dev/null and b/public/img/library-light.jpg differ diff --git a/public/img/library.png b/public/img/library.png deleted file mode 100644 index ecba123..0000000 Binary files a/public/img/library.png and /dev/null differ diff --git a/scripts/stamp-version.js b/scripts/stamp-version.js index 3296196..03b00aa 100644 --- a/scripts/stamp-version.js +++ b/scripts/stamp-version.js @@ -1,11 +1,20 @@ const fs = require('fs'); +const { createHash } = require('node:crypto'); const { version } = require('../package.json'); -const assetVersion = process.env.BUILD_REVISION || version; +const stylesheet = fs.readFileSync('build/css/style.css', 'utf8'); +const themeScript = fs.readFileSync('build/js/theme.js', 'utf8'); + +function versionAsset(url) { + const path = url.startsWith('public/') ? url : `build/${url}`; + const hash = createHash('sha256').update(fs.readFileSync(path)).digest('hex').slice(0, 16); + return `${url}?v=${hash}`; +} for (const file of ['build/index.html', 'build/status/index.html']) { let html = fs.readFileSync(file, 'utf8'); - html = html.replace(/css\/style\.css/g, `css/style.css?v=${assetVersion}`); - html = html.replace(/js\/(main|theme|services|downloads|status)\.js/g, (_, name) => `js/${name}.js?v=${assetVersion}`); + html = html.replace(/`); + html = html.replace(//, () => ``); + html = html.replace(/(?:css|js|public)\/[\w./-]+\.(?:css|js|svg|webp|jpg|png|ico|webmanifest)\b/g, versionAsset); fs.writeFileSync(file, html); } fs.writeFileSync('build/version.json', JSON.stringify({ version, revision: process.env.BUILD_REVISION || null }) + '\n'); diff --git a/scss/_site.scss b/scss/_site.scss index 0735bb0..f718eb8 100644 --- a/scss/_site.scss +++ b/scss/_site.scss @@ -34,6 +34,21 @@ h2 { font-size: clamp(30px, 3.1vw, 43px); } h3 { font-size: 25px; letter-spacing: -.025em; } .hero { padding-top: 74px; } .hero-copy { max-width: 900px; } +.marker-emphasis { position: relative; display: inline-block; isolation: isolate; } +.marker-emphasis::after { + content: ''; + position: absolute; + z-index: 1; + left: -.07em; + right: -.08em; + bottom: -.04em; + height: .4em; + background: var(--accent); + opacity: .75; + pointer-events: none; + transform: rotate(-2deg); + mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 60' preserveAspectRatio='none'%3E%3Cpath d='M5 20L16 16L13 20C65 8 104 7 145 12S235 18 290 4L286 11L299 8L294 20L300 23L291 31L295 36C243 46 196 42 148 37S53 40 3 51L8 41L0 43L6 31L2 29Z'/%3E%3Cpath opacity='.55' d='M11 50C67 37 104 39 151 43S237 50 285 39L280 45C228 55 191 51 148 47S58 45 9 55Z'/%3E%3C/svg%3E") center / 100% 100% no-repeat; +} .hero-description { max-width: 660px; color: var(--muted); font-size: 20px; line-height: 1.6; margin-top: 26px; } .hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 28px; margin-top: 32px; } .button { display: inline-flex; align-items: center; justify-content: center; gap: 22px; padding: 13px 24px; min-height: 50px; border-radius: 28px; text-decoration: none; font-size: 16px; font-weight: 600; } @@ -41,9 +56,12 @@ h3 { font-size: 25px; letter-spacing: -.025em; } .button-primary:hover { color: var(--on-accent); filter: brightness(.94); } .text-link { display: inline-flex; align-items: center; gap: 10px; font-size: 15px; font-weight: 500; text-decoration: none; } .library-preview { margin: 52px 0 0; } -.library-preview > a { display: block; border-radius: 14px; box-shadow: var(--shadow); } -.library-preview img { width: 100%; height: auto; border: 1px solid var(--border); border-radius: 14px; background: #222124; } -.library-preview figcaption { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px 20px; margin-top: 14px; color: var(--muted); font-size: 13px; } +.library-preview > a { display: block; border-radius: 14px; box-shadow: var(--shadow); cursor: zoom-in; } +.library-preview img { width: 100%; height: auto; border: 1px solid var(--border); border-radius: 14px; background: var(--surface); } +.library-dialog { margin: auto; padding: 0; width: fit-content; max-width: calc(100vw - 32px); max-height: calc(100dvh - 32px); border: 0; background: transparent; color: var(--text); } +.library-dialog::backdrop { background: rgb(0 0 0 / .75); } +.library-dialog img { display: block; width: auto; height: auto; max-width: 100%; max-height: calc(100dvh - 32px); margin: auto; border-radius: 6px; object-fit: contain; cursor: zoom-out; } +html:has(.library-dialog[open]) { overflow: hidden; } .product { padding-block: 88px; } .product > h2 { max-width: 780px; margin-bottom: 36px; } .feature-panels { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; } diff --git a/status/index.html b/status/index.html index caabed1..c061145 100644 --- a/status/index.html +++ b/status/index.html @@ -15,7 +15,7 @@ content="Check the current availability of the Papyrus API and sync services."> - +