feat(album-detail): album header gradient backdrop from artwork palette
Extracts the dominant color from album artwork (via node-vibrant) and paints it as a soft top-down gradient behind the album header, with an appearance toggle to opt out. Adds the .has-art-bg state class plus contrast-lifted overrides for back button, info text, and artist link. cc
This commit is contained in:
11 files changed
+1024
-1
No files matched your search
Generated
+745
@@ -19,6 +19,7 @@
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"lucide-svelte": "^1.0.1",
|
||||
"node-vibrant": "^4.0.4",
|
||||
"svelte-i18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -804,6 +805,134 @@
|
||||
"svelte": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/bmp": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.22.12.tgz",
|
||||
"integrity": "sha512-aeI64HD0npropd+AR76MCcvvRaa+Qck6loCOS03CkkxGHN5/r336qTM5HPUdHKMDOGzqknuVPA8+kK1t03z12g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"bmp-js": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/core": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.12.tgz",
|
||||
"integrity": "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"any-base": "^1.1.0",
|
||||
"buffer": "^5.2.0",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-type": "^16.5.4",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"tinycolor2": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/custom": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
|
||||
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/core": "^0.22.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/gif": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.22.12.tgz",
|
||||
"integrity": "sha512-y6BFTJgch9mbor2H234VSjd9iwAhaNf/t3US5qpYIs0TSbAvM02Fbc28IaDETj9+4YB4676sz4RcN/zwhfu1pg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"gifwrap": "^0.10.1",
|
||||
"omggif": "^1.0.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/jpeg": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz",
|
||||
"integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"jpeg-js": "^0.4.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/plugin-resize": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
|
||||
"integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/png": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz",
|
||||
"integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/utils": "^0.22.12",
|
||||
"pngjs": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/tiff": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.22.12.tgz",
|
||||
"integrity": "sha512-E1LtMh4RyJsoCAfAkBRVSYyZDTtLq9p9LUiiYP0vPtXyxX4BiYBUYihTLSBlCQg5nF2e4OpQg7SPrLdJ66u7jg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"utif2": "^4.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/types": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.22.12.tgz",
|
||||
"integrity": "sha512-wwKYzRdElE1MBXFREvCto5s699izFHNVvALUv79GXNbsOVqlwlOxlWJ8DuyOGIXoLP4JW/m30YyuTtfUJgMRMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/bmp": "^0.22.12",
|
||||
"@jimp/gif": "^0.22.12",
|
||||
"@jimp/jpeg": "^0.22.12",
|
||||
"@jimp/png": "^0.22.12",
|
||||
"@jimp/tiff": "^0.22.12",
|
||||
"timm": "^1.6.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@jimp/custom": ">=0.3.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@jimp/utils": {
|
||||
"version": "0.22.12",
|
||||
"resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.12.tgz",
|
||||
"integrity": "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
@@ -1562,6 +1691,12 @@
|
||||
"svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tokenizer/token": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
|
||||
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
@@ -1628,6 +1763,156 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/color": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/color/-/color-4.0.4.tgz",
|
||||
"integrity": "sha512-Fq2tAszz4QOPWfHZ+KuEAchXUD8i594BM2fOJt8dI/fvYbiVoBycBF/BlNH6F4IWBubxXoPqD4JmmAHvFYbNew==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/core": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/core/-/core-4.0.4.tgz",
|
||||
"integrity": "sha512-yZ0XSpW2biKyaJPpBC31AVYgn7NseKSO2q3KNMmDrkL2qC6TEWsBMnSQ28n0m///chZELXpQLx1CCOsWg5pj8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4",
|
||||
"@vibrant/generator": "^4.0.4",
|
||||
"@vibrant/image": "^4.0.4",
|
||||
"@vibrant/quantizer": "^4.0.4",
|
||||
"@vibrant/worker": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/generator": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/generator/-/generator-4.0.4.tgz",
|
||||
"integrity": "sha512-rwq8PnlpKdch4YqaA1FAwdm71gKE2cMrUsbu72TqRFGa8rpP1roaZlQCVXIIwElXVc3r9axZyAcqyTLaMjhrTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4",
|
||||
"@vibrant/types": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/generator-default": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/generator-default/-/generator-default-4.0.4.tgz",
|
||||
"integrity": "sha512-QeVDeH2dz9lityvJCb84Ml4hlBTElwCpU7SVpiDFBh6gPoCLnzcb1H9G4NgG3hOlAPyrBM+Ivq1Pg+1lZj5Ywg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4",
|
||||
"@vibrant/generator": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/image": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/image/-/image-4.0.4.tgz",
|
||||
"integrity": "sha512-NBIJj7umfDRVpFjJHQo1AFSCWCzQyjfil+Yxu7W62PEL72GPCif0CDiglPkvVF8QhDLmnx/x1k3LIBb9jWF2sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/image-browser": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/image-browser/-/image-browser-4.0.4.tgz",
|
||||
"integrity": "sha512-7qVyAm+z9t98iwMDzUgGCwgRg0KBB5RXQFgiO2Um5Izd1wO7BKC0SHVEz2k7sRx3XNfBf+JExp8quPrvSz17gg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/image": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/image-node": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/image-node/-/image-node-4.0.4.tgz",
|
||||
"integrity": "sha512-aG8Ukt9oTa6FWaAV5oBKsBetkKASWH31hZiFJ2R1291f3TZlphUyQTJz5TubucIRsCEl4dgG1xyxFPgse2IABA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jimp/custom": "^0.22.12",
|
||||
"@jimp/plugin-resize": "^0.22.12",
|
||||
"@jimp/types": "^0.22.12",
|
||||
"@vibrant/image": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/quantizer": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/quantizer/-/quantizer-4.0.4.tgz",
|
||||
"integrity": "sha512-722CooC2W4mlBiv+zyAsIrIvARnMCN/P2Muo8bnWd0SQlVWFtQnFxJWGOUPOPS4DGe3pGoqmNfvS0let4dICZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4",
|
||||
"@vibrant/image": "^4.0.4",
|
||||
"@vibrant/types": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/quantizer-mmcq": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/quantizer-mmcq/-/quantizer-mmcq-4.0.4.tgz",
|
||||
"integrity": "sha512-/1CNnM96J8K+OBCWNUzywo6VdnmdFJyiKO+ty/nkfe8H0NseOEHIL7PrVtWGgtsb0rh2uTAq2rjXv65TfgPy8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/color": "^4.0.4",
|
||||
"@vibrant/image": "^4.0.4",
|
||||
"@vibrant/quantizer": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/types": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/types/-/types-4.0.4.tgz",
|
||||
"integrity": "sha512-Qq3mVTJamn7yD4OBgBEUKaxfDlm3sxBK55N7dH3XzI9Ey7KR00R06uwtqOcEJMsziWTEXdYN3VUlYaj2Tkt7hw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vibrant/worker": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@vibrant/worker/-/worker-4.0.4.tgz",
|
||||
"integrity": "sha512-Q/R6PYhSMWCXEk/IcXbWIzIu7Z4b58ABkGvcdF8Y+q/7g+KnpxKW5x/jfQ/6ciyYSby13wZZoEdNr3QQVgsdBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vibrant/types": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz",
|
||||
@@ -1751,6 +2036,18 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
@@ -1786,6 +2083,12 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/any-base": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz",
|
||||
"integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
@@ -1815,6 +2118,26 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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/bidi-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
|
||||
@@ -1825,6 +2148,36 @@
|
||||
"require-from-string": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/bmp-js": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
|
||||
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"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/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
@@ -2140,6 +2493,29 @@
|
||||
"es5-ext": "~0.10.14"
|
||||
}
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/exif-parser": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz",
|
||||
"integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
@@ -2176,6 +2552,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/file-type": {
|
||||
"version": "16.5.4",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
|
||||
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-web-to-node-stream": "^3.0.0",
|
||||
"strtok3": "^6.2.4",
|
||||
"token-types": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -2190,6 +2583,16 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gifwrap": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz",
|
||||
"integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"image-q": "^4.0.0",
|
||||
"omggif": "^1.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/globalyzer": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz",
|
||||
@@ -2215,6 +2618,41 @@
|
||||
"node": "^20.19.0 || ^22.12.0 || >=24.0.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==",
|
||||
"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/image-q": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz",
|
||||
"integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "16.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/image-q/node_modules/@types/node": {
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
|
||||
"integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/intl-messageformat": {
|
||||
"version": "10.7.18",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz",
|
||||
@@ -2249,6 +2687,22 @@
|
||||
"@types/estree": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/isomorphic-fetch": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
|
||||
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.1",
|
||||
"whatwg-fetch": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jpeg-js": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
|
||||
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -2676,6 +3130,81 @@
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"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==",
|
||||
"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/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch/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==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/node-fetch/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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-vibrant": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/node-vibrant/-/node-vibrant-4.0.4.tgz",
|
||||
"integrity": "sha512-hA/pUXBE9TJ41G9FlTkzeqD5JdxgvvPGYZb/HNpdkaxxXUEnP36imSolZ644JuPun+lTd+FpWWtBpTYdp2noQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.15.3",
|
||||
"@vibrant/core": "^4.0.4",
|
||||
"@vibrant/generator-default": "^4.0.4",
|
||||
"@vibrant/image-browser": "^4.0.4",
|
||||
"@vibrant/image-node": "^4.0.4",
|
||||
"@vibrant/quantizer-mmcq": "^4.0.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/crutchcorn"
|
||||
}
|
||||
},
|
||||
"node_modules/node-vibrant/node_modules/@types/node": {
|
||||
"version": "18.19.130",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
|
||||
"integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/node-vibrant/node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||
@@ -2686,6 +3215,18 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/omggif": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
|
||||
"integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parse5": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
|
||||
@@ -2706,6 +3247,19 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/peek-readable": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
|
||||
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -2724,6 +3278,36 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pixelmatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz",
|
||||
"integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pngjs": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pixelmatch": "bin/pixelmatch"
|
||||
}
|
||||
},
|
||||
"node_modules/pixelmatch/node_modules/pngjs": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
|
||||
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz",
|
||||
"integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
|
||||
@@ -2767,6 +3351,15 @@
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -2784,6 +3377,62 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"process": "^0.11.10",
|
||||
"string_decoder": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"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/readable-web-to-node-stream": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz",
|
||||
"integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "^4.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
@@ -2798,6 +3447,12 @@
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
@@ -2853,6 +3508,26 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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/saxes": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||
@@ -2916,6 +3591,32 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strtok3": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
|
||||
"integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tokenizer/token": "^0.3.0",
|
||||
"peek-readable": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte": {
|
||||
"version": "5.55.5",
|
||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.5.tgz",
|
||||
@@ -3020,6 +3721,12 @@
|
||||
"node": ">=0.12"
|
||||
}
|
||||
},
|
||||
"node_modules/timm": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz",
|
||||
"integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-glob": {
|
||||
"version": "0.2.9",
|
||||
"resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz",
|
||||
@@ -3037,6 +3744,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
|
||||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
|
||||
@@ -3093,6 +3806,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/token-types": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
|
||||
"integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tokenizer/token": "^0.3.0",
|
||||
"ieee754": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Borewit"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
@@ -3164,6 +3894,15 @@
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/utif2": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz",
|
||||
"integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.0.10",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
||||
@@ -3373,6 +4112,12 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-fetch": {
|
||||
"version": "3.6.20",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
|
||||
"integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"lucide-svelte": "^1.0.1",
|
||||
"node-vibrant": "^4.0.4",
|
||||
"svelte-i18n": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
import BookletViewer from '../BookletViewer.svelte';
|
||||
import type { QobuzGoody } from '$lib/types';
|
||||
import { applyShiftRange, isSelectAllShortcut } from '$lib/utils/multiSelect';
|
||||
import { extractPalette, pickHeaderColor, type ArtworkPalette } from '$lib/utils/artworkPalette';
|
||||
import {
|
||||
subscribe as subscribeAppearance,
|
||||
isAlbumHeaderGradientEnabled,
|
||||
} from '$lib/stores/appearancePreferencesStore';
|
||||
|
||||
interface Track {
|
||||
id: number;
|
||||
@@ -202,6 +207,25 @@
|
||||
let coverOverride = $state<string | null>(null);
|
||||
let scrollContainer: HTMLDivElement | null = $state(null);
|
||||
|
||||
// Header gradient driven by extracted artwork palette.
|
||||
let gradientEnabled = $state(isAlbumHeaderGradientEnabled());
|
||||
let artworkPalette = $state<ArtworkPalette | null>(null);
|
||||
$effect(() => {
|
||||
const url = coverOverride ?? album.artwork ?? null;
|
||||
artworkPalette = null;
|
||||
if (!url || !gradientEnabled) return;
|
||||
extractPalette(url).then((p) => {
|
||||
const current = coverOverride ?? album.artwork ?? null;
|
||||
if (current === url) artworkPalette = p;
|
||||
});
|
||||
});
|
||||
const headerColor = $derived(gradientEnabled ? pickHeaderColor(artworkPalette) : null);
|
||||
const headerStyle = $derived.by(() => {
|
||||
if (!headerColor) return '';
|
||||
const needsScrim = headerColor.luminance > 0.6;
|
||||
return `--art-bg: ${headerColor.hex}; --art-scrim: ${needsScrim ? '0.35' : '0'};`;
|
||||
});
|
||||
|
||||
// Multi-select
|
||||
let multiSelectMode = $state(false);
|
||||
let multiSelectedIds = $state(new Set<number>());
|
||||
@@ -492,8 +516,13 @@
|
||||
}
|
||||
});
|
||||
|
||||
const unsubscribeAppearance = subscribeAppearance(() => {
|
||||
gradientEnabled = isAlbumHeaderGradientEnabled();
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe?.();
|
||||
unsubscribeAppearance();
|
||||
};
|
||||
});
|
||||
|
||||
@@ -550,7 +579,7 @@
|
||||
</script>
|
||||
|
||||
<ViewTransition duration={200} distance={12} direction="up">
|
||||
<div class="album-detail" bind:this={scrollContainer} onscroll={(e) => saveScrollPosition('album', (e.target as HTMLElement).scrollTop, album.id)}>
|
||||
<div class="album-detail" class:has-art-bg={!!headerColor} style={headerStyle} bind:this={scrollContainer} onscroll={(e) => saveScrollPosition('album', (e.target as HTMLElement).scrollTop, album.id)}>
|
||||
<!-- Back Navigation -->
|
||||
<button class="back-btn" onclick={onBack}>
|
||||
<ArrowLeft size={16} />
|
||||
@@ -965,6 +994,47 @@
|
||||
height: 100%;
|
||||
padding: 8px 8px 100px 18px;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.album-detail.has-art-bg::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 320px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0, 0, 0, var(--art-scrim, 0)) 0%, rgba(0, 0, 0, 0) 100%),
|
||||
linear-gradient(180deg, var(--art-bg, transparent) 0%, var(--art-bg, transparent) 30%, transparent 100%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
transition: background 320ms ease;
|
||||
}
|
||||
|
||||
.album-detail > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Lift secondary text contrast over the colored backdrop. */
|
||||
.album-detail.has-art-bg .back-btn,
|
||||
.album-detail.has-art-bg .album-info,
|
||||
.album-detail.has-art-bg .album-quality,
|
||||
.album-detail.has-art-bg .album-stats {
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
}
|
||||
|
||||
.album-detail.has-art-bg .back-btn:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.album-detail.has-art-bg .artist-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.album-detail.has-art-bg .artist-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Custom scrollbar */
|
||||
|
||||
@@ -84,6 +84,10 @@
|
||||
isDegraded
|
||||
} from '$lib/stores/degradedStore';
|
||||
import { showToast } from '$lib/stores/toastStore';
|
||||
import {
|
||||
isAlbumHeaderGradientEnabled,
|
||||
setAlbumHeaderGradient,
|
||||
} from '$lib/stores/appearancePreferencesStore';
|
||||
import {
|
||||
enableVerboseCapture,
|
||||
disableVerboseCapture,
|
||||
@@ -996,6 +1000,12 @@
|
||||
|
||||
// Appearance settings
|
||||
let theme = $state('Dark');
|
||||
let albumHeaderGradient = $state(isAlbumHeaderGradientEnabled());
|
||||
|
||||
function handleAlbumHeaderGradientToggle(enabled: boolean) {
|
||||
setAlbumHeaderGradient(enabled);
|
||||
albumHeaderGradient = enabled;
|
||||
}
|
||||
let toastsEnabled = $state(true);
|
||||
let systemNotificationsEnabled = $state(true);
|
||||
let language = $state('Auto');
|
||||
@@ -4502,6 +4512,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-label">{$t('settings.appearance.albumHeaderGradient')}</span>
|
||||
<small class="setting-note">{$t('settings.appearance.albumHeaderGradientDesc')}</small>
|
||||
</div>
|
||||
<Toggle enabled={albumHeaderGradient} onchange={handleAlbumHeaderGradientToggle} />
|
||||
</div>
|
||||
|
||||
<!-- Auto-Theme generating overlay -->
|
||||
{#if autoThemeGenerating}
|
||||
<div class="auto-theme-overlay">
|
||||
|
||||
@@ -2132,6 +2132,8 @@
|
||||
"appearance": {
|
||||
"title": "Darstellung",
|
||||
"theme": "Thema",
|
||||
"albumHeaderGradient": "Farbverlauf im Album-Header",
|
||||
"albumHeaderGradientDesc": "Verwende die dominante Farbe des Albumcovers als Hintergrund in der Albumdetailansicht.",
|
||||
"themes": {
|
||||
"dark": "Dunkel",
|
||||
"light": "Hell",
|
||||
|
||||
@@ -2132,6 +2132,8 @@
|
||||
"appearance": {
|
||||
"title": "Appearance",
|
||||
"theme": "Theme",
|
||||
"albumHeaderGradient": "Album header gradient",
|
||||
"albumHeaderGradientDesc": "Use the album cover's dominant color as a backdrop in album detail views.",
|
||||
"themes": {
|
||||
"dark": "Dark",
|
||||
"light": "Light",
|
||||
|
||||
@@ -2132,6 +2132,8 @@
|
||||
"appearance": {
|
||||
"title": "Apariencia",
|
||||
"theme": "Tema",
|
||||
"albumHeaderGradient": "Degradado en el encabezado del álbum",
|
||||
"albumHeaderGradientDesc": "Usa el color dominante de la portada como fondo en la vista de detalle del álbum.",
|
||||
"themes": {
|
||||
"dark": "Oscuro",
|
||||
"light": "Claro",
|
||||
|
||||
@@ -2132,6 +2132,8 @@
|
||||
"appearance": {
|
||||
"title": "Apparence",
|
||||
"theme": "Thème",
|
||||
"albumHeaderGradient": "Dégradé dans l'en-tête de l'album",
|
||||
"albumHeaderGradientDesc": "Utilise la couleur dominante de la pochette comme arrière-plan dans la vue détaillée de l'album.",
|
||||
"themes": {
|
||||
"dark": "Sombre",
|
||||
"light": "Clair",
|
||||
|
||||
@@ -2132,6 +2132,8 @@
|
||||
"appearance": {
|
||||
"title": "Aparência",
|
||||
"theme": "Tema",
|
||||
"albumHeaderGradient": "Gradiente no cabeçalho do álbum",
|
||||
"albumHeaderGradientDesc": "Usa a cor dominante da capa como fundo na visualização de detalhes do álbum.",
|
||||
"themes": {
|
||||
"dark": "Escuro",
|
||||
"light": "Claro",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Appearance Preferences Store
|
||||
*
|
||||
* Per-user toggles for visual treatments that have a clear opt-in/opt-out
|
||||
* value (gradients, blurs, motion). Persisted in localStorage.
|
||||
*/
|
||||
|
||||
export interface AppearancePreferences {
|
||||
/** Render the artwork-derived gradient on album detail headers. */
|
||||
albumHeaderGradient: boolean;
|
||||
}
|
||||
|
||||
const STORAGE_KEY = 'qbz-appearance-preferences';
|
||||
|
||||
const DEFAULT_SETTINGS: AppearancePreferences = {
|
||||
albumHeaderGradient: true,
|
||||
};
|
||||
|
||||
let settings: AppearancePreferences = loadSettings();
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function notifyListeners(): void {
|
||||
for (const listener of listeners) listener();
|
||||
}
|
||||
|
||||
function loadSettings(): AppearancePreferences {
|
||||
try {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved) as Partial<AppearancePreferences>;
|
||||
return { ...DEFAULT_SETTINGS, ...parsed };
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to load appearance preferences:', err);
|
||||
}
|
||||
return { ...DEFAULT_SETTINGS };
|
||||
}
|
||||
|
||||
function saveSettings(): void {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
||||
} catch (err) {
|
||||
console.error('Failed to save appearance preferences:', err);
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribe(listener: () => void): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
}
|
||||
|
||||
export function getSettings(): AppearancePreferences {
|
||||
return settings;
|
||||
}
|
||||
|
||||
export function isAlbumHeaderGradientEnabled(): boolean {
|
||||
return settings.albumHeaderGradient;
|
||||
}
|
||||
|
||||
export function setAlbumHeaderGradient(enabled: boolean): void {
|
||||
if (settings.albumHeaderGradient === enabled) return;
|
||||
settings = { ...settings, albumHeaderGradient: enabled };
|
||||
saveSettings();
|
||||
notifyListeners();
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { Vibrant } from 'node-vibrant/browser';
|
||||
|
||||
export interface ArtworkSwatch {
|
||||
hex: string;
|
||||
rgb: [number, number, number];
|
||||
population: number;
|
||||
/** Perceived luminance in 0..1 (Rec. 709). */
|
||||
luminance: number;
|
||||
}
|
||||
|
||||
export interface ArtworkPalette {
|
||||
/** Most populous swatch — closest to a "background" pick. */
|
||||
dominant: ArtworkSwatch | null;
|
||||
vibrant: ArtworkSwatch | null;
|
||||
muted: ArtworkSwatch | null;
|
||||
lightVibrant: ArtworkSwatch | null;
|
||||
darkVibrant: ArtworkSwatch | null;
|
||||
lightMuted: ArtworkSwatch | null;
|
||||
darkMuted: ArtworkSwatch | null;
|
||||
}
|
||||
|
||||
const cache = new Map<string, Promise<ArtworkPalette>>();
|
||||
|
||||
function relativeLuminance(r: number, g: number, b: number): number {
|
||||
const channel = (c: number) => {
|
||||
const v = c / 255;
|
||||
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||
};
|
||||
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
||||
}
|
||||
|
||||
function toSwatch(s: { rgb: [number, number, number]; population: number; hex: string } | null | undefined): ArtworkSwatch | null {
|
||||
if (!s) return null;
|
||||
const [r, g, b] = s.rgb;
|
||||
return {
|
||||
hex: s.hex,
|
||||
rgb: [r, g, b],
|
||||
population: s.population,
|
||||
luminance: relativeLuminance(r, g, b),
|
||||
};
|
||||
}
|
||||
|
||||
export async function extractPalette(url: string | null | undefined): Promise<ArtworkPalette> {
|
||||
if (!url) return emptyPalette();
|
||||
const cached = cache.get(url);
|
||||
if (cached) return cached;
|
||||
|
||||
const promise = (async () => {
|
||||
try {
|
||||
const palette = await Vibrant.from(url).getPalette();
|
||||
const all = [
|
||||
palette.Vibrant,
|
||||
palette.Muted,
|
||||
palette.LightVibrant,
|
||||
palette.DarkVibrant,
|
||||
palette.LightMuted,
|
||||
palette.DarkMuted,
|
||||
].filter((s): s is NonNullable<typeof s> => !!s);
|
||||
const dominantRaw = all.length > 0
|
||||
? all.reduce((a, b) => (a.population >= b.population ? a : b))
|
||||
: null;
|
||||
return {
|
||||
dominant: toSwatch(dominantRaw),
|
||||
vibrant: toSwatch(palette.Vibrant),
|
||||
muted: toSwatch(palette.Muted),
|
||||
lightVibrant: toSwatch(palette.LightVibrant),
|
||||
darkVibrant: toSwatch(palette.DarkVibrant),
|
||||
lightMuted: toSwatch(palette.LightMuted),
|
||||
darkMuted: toSwatch(palette.DarkMuted),
|
||||
} satisfies ArtworkPalette;
|
||||
} catch (err) {
|
||||
console.warn('[artworkPalette] extract failed for', url, err);
|
||||
return emptyPalette();
|
||||
}
|
||||
})();
|
||||
|
||||
cache.set(url, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the swatch most appropriate for an album header backdrop:
|
||||
* darkVibrant when it carries enough weight (≥ 10% of total swatch population),
|
||||
* else darkMuted, else dominant. Returns null when the palette is empty.
|
||||
*/
|
||||
export function pickHeaderColor(palette: ArtworkPalette | null): ArtworkSwatch | null {
|
||||
if (!palette) return null;
|
||||
const swatches = [
|
||||
palette.vibrant,
|
||||
palette.muted,
|
||||
palette.lightVibrant,
|
||||
palette.darkVibrant,
|
||||
palette.lightMuted,
|
||||
palette.darkMuted,
|
||||
];
|
||||
const totalPop = swatches.reduce((sum, s) => sum + (s?.population ?? 0), 0);
|
||||
if (palette.darkVibrant && totalPop > 0 && palette.darkVibrant.population / totalPop >= 0.1) {
|
||||
return palette.darkVibrant;
|
||||
}
|
||||
if (palette.darkMuted) return palette.darkMuted;
|
||||
return palette.dominant;
|
||||
}
|
||||
|
||||
function emptyPalette(): ArtworkPalette {
|
||||
return {
|
||||
dominant: null,
|
||||
vibrant: null,
|
||||
muted: null,
|
||||
lightVibrant: null,
|
||||
darkVibrant: null,
|
||||
lightMuted: null,
|
||||
darkMuted: null,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user