mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
Fix: Reader CSS & TitleBar Controls + WIP Feature
This commit is contained in:
@@ -30,7 +30,7 @@ General/Misc Bugs:
|
||||
In-Progress:
|
||||
- Enable Cloudflare Bypass (Suwayomi Config) (Requires Patching)
|
||||
|
||||
- Saved needs Reading Frecency-Based Display too.
|
||||
- Working on 3D Display Cards
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -181,7 +181,7 @@ modules:
|
||||
path: .
|
||||
- type: file
|
||||
path: packaging/frontend-dist.tar.gz
|
||||
sha256: a9be4a2b00de496e00fba4d7a113ef12357ea27d94942a96b6edaf8445cada30
|
||||
sha256: 6899362aa25ebca481904c1bdc7bc35dee9369407e41fc9746bea3059910c45b
|
||||
- packaging/cargo-sources.json
|
||||
- type: inline
|
||||
dest: src-tauri/.cargo
|
||||
|
||||
+110
-110
@@ -411,14 +411,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/cc/cc-1.2.59.crate",
|
||||
"sha256": "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283",
|
||||
"dest": "cargo/vendor/cc-1.2.59"
|
||||
"url": "https://static.crates.io/crates/cc/cc-1.2.60.crate",
|
||||
"sha256": "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20",
|
||||
"dest": "cargo/vendor/cc-1.2.60"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/cc-1.2.59",
|
||||
"contents": "{\"package\": \"43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/cc-1.2.60",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -1191,14 +1191,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/fastrand/fastrand-2.4.0.crate",
|
||||
"sha256": "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f",
|
||||
"dest": "cargo/vendor/fastrand-2.4.0"
|
||||
"url": "https://static.crates.io/crates/fastrand/fastrand-2.4.1.crate",
|
||||
"sha256": "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6",
|
||||
"dest": "cargo/vendor/fastrand-2.4.1"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/fastrand-2.4.0",
|
||||
"contents": "{\"package\": \"9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/fastrand-2.4.1",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -1854,14 +1854,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/hashbrown/hashbrown-0.16.1.crate",
|
||||
"sha256": "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100",
|
||||
"dest": "cargo/vendor/hashbrown-0.16.1"
|
||||
"url": "https://static.crates.io/crates/hashbrown/hashbrown-0.17.0.crate",
|
||||
"sha256": "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51",
|
||||
"dest": "cargo/vendor/hashbrown-0.17.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/hashbrown-0.16.1",
|
||||
"contents": "{\"package\": \"4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/hashbrown-0.17.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -1997,14 +1997,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/hyper-rustls/hyper-rustls-0.27.7.crate",
|
||||
"sha256": "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58",
|
||||
"dest": "cargo/vendor/hyper-rustls-0.27.7"
|
||||
"url": "https://static.crates.io/crates/hyper-rustls/hyper-rustls-0.27.8.crate",
|
||||
"sha256": "c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b",
|
||||
"dest": "cargo/vendor/hyper-rustls-0.27.8"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/hyper-rustls-0.27.7",
|
||||
"contents": "{\"package\": \"c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/hyper-rustls-0.27.8",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -2231,14 +2231,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/indexmap/indexmap-2.13.1.crate",
|
||||
"sha256": "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff",
|
||||
"dest": "cargo/vendor/indexmap-2.13.1"
|
||||
"url": "https://static.crates.io/crates/indexmap/indexmap-2.14.0.crate",
|
||||
"sha256": "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9",
|
||||
"dest": "cargo/vendor/indexmap-2.14.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/indexmap-2.13.1",
|
||||
"contents": "{\"package\": \"d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/indexmap-2.14.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -2400,14 +2400,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/js-sys/js-sys-0.3.94.crate",
|
||||
"sha256": "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9",
|
||||
"dest": "cargo/vendor/js-sys-0.3.94"
|
||||
"url": "https://static.crates.io/crates/js-sys/js-sys-0.3.95.crate",
|
||||
"sha256": "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca",
|
||||
"dest": "cargo/vendor/js-sys-0.3.95"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/js-sys-0.3.94",
|
||||
"contents": "{\"package\": \"2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/js-sys-0.3.95",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -2530,14 +2530,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/libredox/libredox-0.1.15.crate",
|
||||
"sha256": "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08",
|
||||
"dest": "cargo/vendor/libredox-0.1.15"
|
||||
"url": "https://static.crates.io/crates/libredox/libredox-0.1.16.crate",
|
||||
"sha256": "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c",
|
||||
"dest": "cargo/vendor/libredox-0.1.16"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/libredox-0.1.15",
|
||||
"contents": "{\"package\": \"e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/libredox-0.1.16",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -3206,14 +3206,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/openssl/openssl-0.10.76.crate",
|
||||
"sha256": "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf",
|
||||
"dest": "cargo/vendor/openssl-0.10.76"
|
||||
"url": "https://static.crates.io/crates/openssl/openssl-0.10.77.crate",
|
||||
"sha256": "bfe4646e360ec77dff7dde40ed3d6c5fee52d156ef4a62f53973d38294dad87f",
|
||||
"dest": "cargo/vendor/openssl-0.10.77"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/openssl-0.10.76",
|
||||
"contents": "{\"package\": \"bfe4646e360ec77dff7dde40ed3d6c5fee52d156ef4a62f53973d38294dad87f\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/openssl-0.10.77",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -3245,14 +3245,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/openssl-sys/openssl-sys-0.9.112.crate",
|
||||
"sha256": "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb",
|
||||
"dest": "cargo/vendor/openssl-sys-0.9.112"
|
||||
"url": "https://static.crates.io/crates/openssl-sys/openssl-sys-0.9.113.crate",
|
||||
"sha256": "ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644",
|
||||
"dest": "cargo/vendor/openssl-sys-0.9.113"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/openssl-sys-0.9.112",
|
||||
"contents": "{\"package\": \"ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/openssl-sys-0.9.113",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -3635,14 +3635,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/pkg-config/pkg-config-0.3.32.crate",
|
||||
"sha256": "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c",
|
||||
"dest": "cargo/vendor/pkg-config-0.3.32"
|
||||
"url": "https://static.crates.io/crates/pkg-config/pkg-config-0.3.33.crate",
|
||||
"sha256": "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e",
|
||||
"dest": "cargo/vendor/pkg-config-0.3.33"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/pkg-config-0.3.32",
|
||||
"contents": "{\"package\": \"19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/pkg-config-0.3.33",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -3986,14 +3986,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/rand/rand-0.9.2.crate",
|
||||
"sha256": "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1",
|
||||
"dest": "cargo/vendor/rand-0.9.2"
|
||||
"url": "https://static.crates.io/crates/rand/rand-0.9.3.crate",
|
||||
"sha256": "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166",
|
||||
"dest": "cargo/vendor/rand-0.9.3"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rand-0.9.2",
|
||||
"contents": "{\"package\": \"7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rand-0.9.3",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -4155,14 +4155,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/redox_syscall/redox_syscall-0.7.3.crate",
|
||||
"sha256": "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16",
|
||||
"dest": "cargo/vendor/redox_syscall-0.7.3"
|
||||
"url": "https://static.crates.io/crates/redox_syscall/redox_syscall-0.7.4.crate",
|
||||
"sha256": "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a",
|
||||
"dest": "cargo/vendor/redox_syscall-0.7.4"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/redox_syscall-0.7.3",
|
||||
"contents": "{\"package\": \"f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/redox_syscall-0.7.4",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -4337,14 +4337,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/rustls/rustls-0.23.37.crate",
|
||||
"sha256": "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4",
|
||||
"dest": "cargo/vendor/rustls-0.23.37"
|
||||
"url": "https://static.crates.io/crates/rustls/rustls-0.23.38.crate",
|
||||
"sha256": "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21",
|
||||
"dest": "cargo/vendor/rustls-0.23.38"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rustls-0.23.37",
|
||||
"contents": "{\"package\": \"69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rustls-0.23.38",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -4402,14 +4402,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/rustls-webpki/rustls-webpki-0.103.10.crate",
|
||||
"sha256": "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef",
|
||||
"dest": "cargo/vendor/rustls-webpki-0.103.10"
|
||||
"url": "https://static.crates.io/crates/rustls-webpki/rustls-webpki-0.103.11.crate",
|
||||
"sha256": "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4",
|
||||
"dest": "cargo/vendor/rustls-webpki-0.103.11"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rustls-webpki-0.103.10",
|
||||
"contents": "{\"package\": \"20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/rustls-webpki-0.103.11",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -5668,14 +5668,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/tokio/tokio-1.51.0.crate",
|
||||
"sha256": "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd",
|
||||
"dest": "cargo/vendor/tokio-1.51.0"
|
||||
"url": "https://static.crates.io/crates/tokio/tokio-1.51.1.crate",
|
||||
"sha256": "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c",
|
||||
"dest": "cargo/vendor/tokio-1.51.1"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/tokio-1.51.0",
|
||||
"contents": "{\"package\": \"f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/tokio-1.51.1",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -5824,14 +5824,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/toml_edit/toml_edit-0.25.10+spec-1.1.0.crate",
|
||||
"sha256": "a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b",
|
||||
"dest": "cargo/vendor/toml_edit-0.25.10+spec-1.1.0"
|
||||
"url": "https://static.crates.io/crates/toml_edit/toml_edit-0.25.11+spec-1.1.0.crate",
|
||||
"sha256": "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b",
|
||||
"dest": "cargo/vendor/toml_edit-0.25.11+spec-1.1.0"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"a82418ca169e235e6c399a84e395ab6debeb3bc90edc959bf0f48647c6a32d1b\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/toml_edit-0.25.10+spec-1.1.0",
|
||||
"contents": "{\"package\": \"0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/toml_edit-0.25.11+spec-1.1.0",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -6344,66 +6344,66 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen/wasm-bindgen-0.2.117.crate",
|
||||
"sha256": "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0",
|
||||
"dest": "cargo/vendor/wasm-bindgen-0.2.117"
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen/wasm-bindgen-0.2.118.crate",
|
||||
"sha256": "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89",
|
||||
"dest": "cargo/vendor/wasm-bindgen-0.2.118"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-0.2.117",
|
||||
"contents": "{\"package\": \"0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-0.2.118",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-futures/wasm-bindgen-futures-0.4.67.crate",
|
||||
"sha256": "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e",
|
||||
"dest": "cargo/vendor/wasm-bindgen-futures-0.4.67"
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-futures/wasm-bindgen-futures-0.4.68.crate",
|
||||
"sha256": "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8",
|
||||
"dest": "cargo/vendor/wasm-bindgen-futures-0.4.68"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-futures-0.4.67",
|
||||
"contents": "{\"package\": \"f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-futures-0.4.68",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-macro/wasm-bindgen-macro-0.2.117.crate",
|
||||
"sha256": "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-0.2.117"
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-macro/wasm-bindgen-macro-0.2.118.crate",
|
||||
"sha256": "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-0.2.118"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-0.2.117",
|
||||
"contents": "{\"package\": \"eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-0.2.118",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-macro-support/wasm-bindgen-macro-support-0.2.117.crate",
|
||||
"sha256": "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.117"
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-macro-support/wasm-bindgen-macro-support-0.2.118.crate",
|
||||
"sha256": "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.118"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.117",
|
||||
"contents": "{\"package\": \"9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.118",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-shared/wasm-bindgen-shared-0.2.117.crate",
|
||||
"sha256": "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b",
|
||||
"dest": "cargo/vendor/wasm-bindgen-shared-0.2.117"
|
||||
"url": "https://static.crates.io/crates/wasm-bindgen-shared/wasm-bindgen-shared-0.2.118.crate",
|
||||
"sha256": "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129",
|
||||
"dest": "cargo/vendor/wasm-bindgen-shared-0.2.118"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-shared-0.2.117",
|
||||
"contents": "{\"package\": \"5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/wasm-bindgen-shared-0.2.118",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
@@ -6461,14 +6461,14 @@
|
||||
{
|
||||
"type": "archive",
|
||||
"archive-type": "tar-gzip",
|
||||
"url": "https://static.crates.io/crates/web-sys/web-sys-0.3.94.crate",
|
||||
"sha256": "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a",
|
||||
"dest": "cargo/vendor/web-sys-0.3.94"
|
||||
"url": "https://static.crates.io/crates/web-sys/web-sys-0.3.95.crate",
|
||||
"sha256": "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d",
|
||||
"dest": "cargo/vendor/web-sys-0.3.95"
|
||||
},
|
||||
{
|
||||
"type": "inline",
|
||||
"contents": "{\"package\": \"cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/web-sys-0.3.94",
|
||||
"contents": "{\"package\": \"4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d\", \"files\": {}}",
|
||||
"dest": "cargo/vendor/web-sys-0.3.95",
|
||||
"dest-filename": ".cargo-checksum.json"
|
||||
},
|
||||
{
|
||||
|
||||
+1
-48
@@ -435,22 +435,6 @@
|
||||
onDismiss={() => { idle = false; resetIdle(); }} />
|
||||
{/if}
|
||||
{#if !store.activeChapter}<TitleBar />{/if}
|
||||
{#if store.activeChapter && isWindows && !store.isFullscreen}
|
||||
<div class="reader-chrome">
|
||||
<button onclick={() => win.minimize()} title="Minimize" aria-label="Minimize">
|
||||
<svg width="10" height="1" viewBox="0 0 10 1"><line x1="0" y1="0.5" x2="10" y2="0.5" stroke="currentColor" stroke-width="1.5" /></svg>
|
||||
</button>
|
||||
<button onclick={() => win.toggleMaximize()} title="Maximize" aria-label="Maximize">
|
||||
<svg width="9" height="9" viewBox="0 0 9 9"><rect x="0.75" y="0.75" width="7.5" height="7.5" rx="1" fill="none" stroke="currentColor" stroke-width="1.5" /></svg>
|
||||
</button>
|
||||
<button class="chrome-close" onclick={() => win.close()} title="Close" aria-label="Close">
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<line x1="1" y1="1" x2="9" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
<line x1="9" y1="1" x2="1" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="content">
|
||||
{#if store.activeChapter}<Reader />{:else}<Layout />{/if}
|
||||
</div>
|
||||
@@ -470,37 +454,6 @@
|
||||
.root { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
|
||||
.content { flex: 1; overflow: hidden; }
|
||||
|
||||
.reader-chrome {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: calc(var(--z-reader) + 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding: 6px 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
.reader-chrome:hover { opacity: 1; }
|
||||
.reader-chrome button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-faint);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: color var(--t-base), background var(--t-base);
|
||||
}
|
||||
.reader-chrome button:hover { color: var(--text-muted); background: rgba(255,255,255,0.08); }
|
||||
.reader-chrome .chrome-close:hover { color: #fff; background: #c0392b; }
|
||||
|
||||
/* Auth overlay — floats above the SplashScreen */
|
||||
.auth-overlay { position: fixed; inset: 0; z-index: 10000; display: flex; align-items: center; justify-content: center; pointer-events: none; }
|
||||
.auth-card { pointer-events: auto; width: min(280px, calc(100vw - 48px)); background: var(--bg-surface); border: 1px solid var(--border-base); border-radius: var(--radius-xl); padding: var(--sp-6) var(--sp-5); display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); box-shadow: 0 32px 80px rgba(0,0,0,0.75); animation: authIn 0.28s cubic-bezier(0.16,1,0.3,1) both; text-align: center; }
|
||||
@keyframes authIn { from { opacity: 0; transform: translateY(10px) scale(0.97); } to { opacity: 1; transform: none; } }
|
||||
@@ -522,4 +475,4 @@
|
||||
.auth-btn:disabled { opacity: 0.35; cursor: default; }
|
||||
.auth-btn--ghost { background: none; border-color: transparent; color: var(--text-faint); font-size: var(--text-xs); padding: 4px; }
|
||||
.auth-btn--ghost:hover:not(:disabled) { color: var(--text-muted); opacity: 1; }
|
||||
</style>
|
||||
</style>
|
||||
|
||||
+175
-107
@@ -14,6 +14,7 @@
|
||||
import { DEFAULT_MANGA_PREFS } from "../../store/state.svelte";
|
||||
import { matchesKeybind, toggleFullscreen, DEFAULT_KEYBINDS } from "../../lib/keybinds";
|
||||
import { setReading } from "../../lib/discord";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import type { FitMode, MarkerColor } from "../../store/state.svelte";
|
||||
|
||||
const AVG_MIN_PER_PAGE = 0.33;
|
||||
@@ -37,6 +38,8 @@
|
||||
const pageCache = new Map<number, string[]>();
|
||||
const inflight = new Map<number, Promise<string[]>>();
|
||||
|
||||
const win = getCurrentWindow();
|
||||
|
||||
const useBlob = $derived((appStore.settings.serverAuthMode ?? "NONE") === "BASIC_AUTH");
|
||||
|
||||
function resolveUrl(url: string, priority = 0): Promise<string> {
|
||||
@@ -123,6 +126,8 @@
|
||||
let error: string | null = $state(null);
|
||||
let dlOpen = $state(false);
|
||||
let zoomOpen = $state(false);
|
||||
let winOpen = $state(false);
|
||||
let isFullscreen = $state(false);
|
||||
let uiVisible = $state(true);
|
||||
let pageReady = $state(false);
|
||||
let pageGroups: number[][] = $state([]);
|
||||
@@ -430,7 +435,11 @@
|
||||
if (store.settings.autoMarkRead && activePage !== null && activeChId) {
|
||||
const chunk = stripChaptersRef.find(c => c.chapterId === activeChId);
|
||||
const total = chunk ? chunk.urls.length : store.pageUrls.length;
|
||||
if (total > 0 && activePage >= total - 1) markChapterRead(activeChId);
|
||||
if (total > 0 && activePage >= total) markChapterRead(activeChId);
|
||||
}
|
||||
if (containerEl.scrollTop + containerEl.clientHeight >= containerEl.scrollHeight - 40) {
|
||||
const last = stripChaptersRef[stripChaptersRef.length - 1];
|
||||
if (last && store.settings.autoMarkRead) markChapterRead(last.chapterId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,6 +730,7 @@
|
||||
markerOpen = !markerOpen;
|
||||
zoomOpen = false;
|
||||
dlOpen = false;
|
||||
winOpen = false;
|
||||
}
|
||||
|
||||
function commitMarker() {
|
||||
@@ -766,11 +776,11 @@
|
||||
function showUi() {
|
||||
uiVisible = true;
|
||||
if (hideTimer) clearTimeout(hideTimer);
|
||||
hideTimer = setTimeout(() => { if (!markerOpen) uiVisible = false; }, 3000);
|
||||
hideTimer = setTimeout(() => { if (!markerOpen && !winOpen) uiVisible = false; }, 3000);
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (markerOpen) {
|
||||
if (markerOpen || winOpen) {
|
||||
uiVisible = true;
|
||||
if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
|
||||
}
|
||||
@@ -829,6 +839,7 @@
|
||||
if (markerOpen) { markerOpen = false; return; }
|
||||
if (zoomOpen) { zoomOpen = false; return; }
|
||||
if (dlOpen) { dlOpen = false; return; }
|
||||
if (winOpen) { winOpen = false; return; }
|
||||
closeReader(); return;
|
||||
}
|
||||
if (e.ctrlKey && (e.key === "=" || e.key === "+")) { e.preventDefault(); adjustZoom(ZOOM_STEP * 2); return; }
|
||||
@@ -895,7 +906,7 @@
|
||||
dlBusy = false; dlOpen = false;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
showUi();
|
||||
window.addEventListener("keydown", onKey);
|
||||
window.addEventListener("wheel", onWheel, { passive: false });
|
||||
@@ -903,6 +914,9 @@
|
||||
window.addEventListener("mouseup", onInspectMouseUp);
|
||||
containerEl?.focus({ preventScroll: true });
|
||||
|
||||
isFullscreen = await win.isFullscreen();
|
||||
const unlistenFs = await win.onResized(async () => { isFullscreen = await win.isFullscreen(); });
|
||||
|
||||
let roTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const ro = new ResizeObserver(entries => {
|
||||
const w = entries[0].contentRect.width;
|
||||
@@ -920,6 +934,7 @@
|
||||
window.removeEventListener("mousemove", onInspectMouseMove);
|
||||
window.removeEventListener("mouseup", onInspectMouseUp);
|
||||
cleanupScroll();
|
||||
unlistenFs();
|
||||
ro.disconnect();
|
||||
};
|
||||
});
|
||||
@@ -992,34 +1007,54 @@
|
||||
</button>
|
||||
|
||||
<div class="mode-extras">
|
||||
<label class="toggle-row">
|
||||
<input type="checkbox" class="sr-only" checked={store.settings.pageGap ?? true} onchange={(e) => updateSettings({ pageGap: e.currentTarget.checked })} />
|
||||
<span class="toggle-label">Gap</span>
|
||||
</label>
|
||||
<label class="toggle-row">
|
||||
<input type="checkbox" class="sr-only" checked={store.settings.optimizeContrast ?? false} onchange={(e) => updateSettings({ optimizeContrast: e.currentTarget.checked })} />
|
||||
<span class="toggle-label">Contrast</span>
|
||||
</label>
|
||||
{#if style === "double"}
|
||||
<button class="mode-btn" class:active={store.settings.offsetDoubleSpreads} onclick={() => updateSettings({ offsetDoubleSpreads: !store.settings.offsetDoubleSpreads })}>
|
||||
<span class="mode-label">Offset</span>
|
||||
</button>
|
||||
{/if}
|
||||
{#if style === "longstrip"}
|
||||
<button class="mode-btn" class:active={store.settings.pageGap} onclick={() => updateSettings({ pageGap: !store.settings.pageGap })}>
|
||||
<span class="mode-label">Gap</span>
|
||||
</button>
|
||||
<button class="mode-btn" class:active={autoNext} onclick={() => updateSettings({ autoNextChapter: !autoNext })}>
|
||||
<span class="mode-label">Auto</span>
|
||||
</button>
|
||||
{/if}
|
||||
{#if !autoNext}
|
||||
<button class="mode-btn" class:active={markOnNext} onclick={() => updateSettings({ markReadOnNext: !markOnNext })}>
|
||||
<span class="mode-label">Mk.Read</span>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="top-sep"></div>
|
||||
|
||||
<div class="dl-wrap">
|
||||
<button class="icon-btn" onclick={() => { dlOpen = !dlOpen; zoomOpen = false; markerOpen = false; }} title="Download">
|
||||
<Download size={15} weight="light" />
|
||||
</button>
|
||||
</div>
|
||||
<button class="mode-btn" onclick={() => dlOpen = true}>
|
||||
<Download size={14} weight="light" />
|
||||
</button>
|
||||
|
||||
<div class="marker-wrap">
|
||||
<button class="icon-btn" class:active={markerOpen || hasMarkerOnPage} onclick={openMarkerPopover} title="Page marker">
|
||||
<MapPin size={15} weight={hasMarkerOnPage ? "fill" : "light"} />
|
||||
<button
|
||||
class="icon-btn"
|
||||
class:active={hasMarkerOnPage}
|
||||
class:marker-btn-has={hasMarkerOnPage}
|
||||
onclick={openMarkerPopover}
|
||||
title={hasMarkerOnPage ? "Edit marker" : "Add marker"}
|
||||
style={hasMarkerOnPage ? `--marker-color:${MARKER_COLOR_HEX[currentPageMarkers[0].color]}` : ""}
|
||||
>
|
||||
<MapPin size={14} weight={hasMarkerOnPage ? "fill" : "regular"} />
|
||||
</button>
|
||||
|
||||
{#if markerOpen}
|
||||
<div class="marker-popover">
|
||||
<div class="marker-popover" role="presentation" onclick={(e) => e.stopPropagation()}
|
||||
onmouseenter={() => { uiVisible = true; if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; } }}
|
||||
>
|
||||
<div class="marker-pop-header">
|
||||
<span class="marker-pop-title">{markerEditId ? "Edit marker" : "Add marker"}</span>
|
||||
<span class="marker-pop-title">
|
||||
{markerEditId ? "Edit marker" : "New marker"} · p.{store.pageNumber}
|
||||
</span>
|
||||
{#if markerEditId}
|
||||
<button class="icon-btn" onclick={deleteCurrentMarker} title="Delete marker"><X size={13} weight="light" /></button>
|
||||
<button class="marker-delete-btn" onclick={deleteCurrentMarker} title="Delete marker">
|
||||
<X size={12} weight="light" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="marker-color-row">
|
||||
@@ -1062,6 +1097,49 @@
|
||||
<button class="icon-btn" class:active={isBookmarked} onclick={toggleBookmark} title={isBookmarked ? "Remove bookmark" : "Bookmark this page"}>
|
||||
<Bookmark size={15} weight={isBookmarked ? "fill" : "regular"} />
|
||||
</button>
|
||||
|
||||
<div class="wc-wrap">
|
||||
<button
|
||||
class="icon-btn"
|
||||
class:active={winOpen}
|
||||
onclick={() => { winOpen = !winOpen; markerOpen = false; zoomOpen = false; dlOpen = false; }}
|
||||
title="Window controls"
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
||||
<circle cx="6" cy="1.5" r="1.2" fill="currentColor"/>
|
||||
<circle cx="6" cy="6" r="1.2" fill="currentColor"/>
|
||||
<circle cx="6" cy="10.5" r="1.2" fill="currentColor"/>
|
||||
</svg>
|
||||
</button>
|
||||
{#if winOpen}
|
||||
<div class="wc-dropdown" role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||
<button class="wc-btn" onclick={() => { winOpen = false; win.minimize(); }}>
|
||||
<svg width="10" height="1" viewBox="0 0 10 1"><line x1="0" y1="0.5" x2="10" y2="0.5" stroke="currentColor" stroke-width="1.5"/></svg>
|
||||
<span>Minimize</span>
|
||||
</button>
|
||||
<button class="wc-btn" onclick={() => { winOpen = false; win.toggleMaximize(); }}>
|
||||
{#if isFullscreen}
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<polyline points="1,4 1,1 4,1" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<polyline points="6,1 9,1 9,4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<polyline points="9,6 9,9 6,9" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<polyline points="4,9 1,9 1,6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
{:else}
|
||||
<svg width="9" height="9" viewBox="0 0 9 9"><rect x="0.75" y="0.75" width="7.5" height="7.5" rx="1" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
|
||||
{/if}
|
||||
<span>{isFullscreen ? "Exit Fullscreen" : "Fullscreen"}</span>
|
||||
</button>
|
||||
<button class="wc-btn wc-close" onclick={() => { winOpen = false; win.close(); }}>
|
||||
<svg width="10" height="10" viewBox="0 0 10 10">
|
||||
<line x1="1" y1="1" x2="9" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<line x1="9" y1="1" x2="1" y2="9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<span>Close</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1172,26 +1250,26 @@
|
||||
onmouseup={() => sliderDragging = false}
|
||||
>
|
||||
<div class="slider-track-bg">
|
||||
<div class="slider-fill" style="width:{sliderPct}%">
|
||||
</div>
|
||||
{#each activeChapterMarkers as marker}
|
||||
{@const pct = sliderMax > 1 ? ((marker.pageNumber - 1) / (sliderMax - 1)) * 100 : 0}
|
||||
{@const adjustedPct = rtl ? 100 - pct : pct}
|
||||
<div
|
||||
class="slider-checkpoint marker-checkpoint"
|
||||
style="left:{adjustedPct}%;background:{MARKER_COLOR_HEX[marker.color]}"
|
||||
title="Marker p.{marker.pageNumber}{marker.note ? ': ' + marker.note : ''}"
|
||||
></div>
|
||||
{/each}
|
||||
{#if currentBookmark && currentBookmark.chapterId === displayChapter?.id}
|
||||
{@const pct = sliderMax > 1 ? ((currentBookmark.pageNumber - 1) / (sliderMax - 1)) * 100 : 0}
|
||||
{@const adjustedPct = rtl ? 100 - pct : pct}
|
||||
<div class="slider-checkpoint bookmark-checkpoint" style="left:{adjustedPct}%" title="Bookmark p.{currentBookmark.pageNumber}"></div>
|
||||
{/if}
|
||||
<div class="slider-fill" style={rtl ? `width:${100 - sliderPct}%;margin-left:auto` : `width:${sliderPct}%`}></div>
|
||||
</div>
|
||||
<div class="slider-thumb" style="left:{sliderPct}%"></div>
|
||||
|
||||
{#if currentBookmark && currentBookmark.chapterId === displayChapter?.id}
|
||||
{@const bOrd = rtl ? lastPage - currentBookmark.pageNumber + 1 : currentBookmark.pageNumber}
|
||||
{@const bPct = lastPage > 1 ? ((bOrd - 1) / (lastPage - 1)) * 100 : 0}
|
||||
<div class="slider-checkpoint bookmark-checkpoint" style="left: {bPct}%" title="Bookmark: Page {currentBookmark.pageNumber}"></div>
|
||||
{/if}
|
||||
|
||||
{#each activeChapterMarkers as m (m.id)}
|
||||
{@const mOrd = rtl ? lastPage - m.pageNumber + 1 : m.pageNumber}
|
||||
{@const mPct = lastPage > 1 ? ((mOrd - 1) / (lastPage - 1)) * 100 : 0}
|
||||
<div class="slider-checkpoint marker-checkpoint" style="left: {mPct}%; background:{MARKER_COLOR_HEX[m.color]}" title="{m.note ? m.note : 'Marker'} · Page {m.pageNumber}"></div>
|
||||
{/each}
|
||||
|
||||
{#if sliderHover || sliderDragging}
|
||||
<div class="slider-tooltip" style="left:{sliderPct}%">p.{sliderPage}</div>
|
||||
<div class="slider-tooltip" style="left: {sliderPct}%">
|
||||
{sliderPage} / {sliderMax}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1201,90 +1279,73 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if dlOpen}
|
||||
<div class="dl-backdrop" role="presentation" onclick={(e) => { if (e.target === e.currentTarget) dlOpen = false; }}>
|
||||
<div class="dl-modal">
|
||||
{#if dlOpen && store.activeChapter}
|
||||
{@const queueable = adjacent.remaining.filter(c => !c.isDownloaded)}
|
||||
<div class="dl-backdrop" role="presentation" onclick={() => dlOpen = false}>
|
||||
<div class="dl-modal" role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||
<p class="dl-title">Download</p>
|
||||
<button class="dl-option" disabled={dlBusy} onclick={() => runDl(() => gql(ENQUEUE_DOWNLOAD, { chapterId: store.activeChapter?.id }))}>
|
||||
<span>This chapter</span>
|
||||
<span class="dl-sub">{store.pageUrls.length} pages</span>
|
||||
<button class="dl-option" disabled={dlBusy || !!store.activeChapter.isDownloaded}
|
||||
onclick={() => runDl(() => gql(ENQUEUE_DOWNLOAD, { chapterId: store.activeChapter!.id }))}>
|
||||
This chapter
|
||||
<span class="dl-sub">{store.activeChapter.isDownloaded ? "Already downloaded" : store.activeChapter.name}</span>
|
||||
</button>
|
||||
{#if adjacent.next}
|
||||
<div class="dl-row">
|
||||
<button class="dl-option" style="flex:1" disabled={dlBusy} onclick={() => {
|
||||
const ids = store.activeChapterList
|
||||
.slice(store.activeChapterList.findIndex(c => c.id === store.activeChapter?.id))
|
||||
.slice(1, 1 + nextN)
|
||||
.map(c => c.id);
|
||||
runDl(() => gql(ENQUEUE_CHAPTERS_DOWNLOAD, { chapterIds: ids }));
|
||||
}}>
|
||||
<span>Next {nextN} chapters</span>
|
||||
<span class="dl-sub">{Math.min(nextN, adjacent.remaining.length)} available</span>
|
||||
</button>
|
||||
<div class="dl-stepper">
|
||||
<button class="dl-step-btn" disabled={nextN <= 1} onclick={() => nextN = Math.max(1, nextN - 1)}>−</button>
|
||||
<span class="dl-step-val">{nextN}</span>
|
||||
<button class="dl-step-btn" disabled={nextN >= 50} onclick={() => nextN = Math.min(50, nextN + 1)}>+</button>
|
||||
</div>
|
||||
<div class="dl-row">
|
||||
<button class="dl-option" disabled={dlBusy || queueable.length === 0}
|
||||
onclick={() => runDl(() => gql(ENQUEUE_CHAPTERS_DOWNLOAD, { chapterIds: queueable.slice(0, nextN).map(c => c.id) }))}>
|
||||
Next chapters
|
||||
<span class="dl-sub">{Math.min(nextN, queueable.length)} not yet downloaded</span>
|
||||
</button>
|
||||
<div class="dl-stepper" role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||
<button class="dl-step-btn" onclick={() => nextN = Math.max(1, nextN - 1)} disabled={nextN <= 1}>−</button>
|
||||
<span class="dl-step-val">{nextN}</span>
|
||||
<button class="dl-step-btn" onclick={() => nextN = Math.min(queueable.length || 1, nextN + 1)} disabled={nextN >= queueable.length}>+</button>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="dl-option" disabled={dlBusy} onclick={() => {
|
||||
const ids = store.activeChapterList.filter(c => !c.isDownloaded).map(c => c.id);
|
||||
runDl(() => gql(ENQUEUE_CHAPTERS_DOWNLOAD, { chapterIds: ids }));
|
||||
}}>
|
||||
<span>All undownloaded</span>
|
||||
<span class="dl-sub">{store.activeChapterList.filter(c => !c.isDownloaded).length} chapters</span>
|
||||
</div>
|
||||
<button class="dl-option" disabled={dlBusy || queueable.length === 0}
|
||||
onclick={() => runDl(() => gql(ENQUEUE_CHAPTERS_DOWNLOAD, { chapterIds: queueable.map(c => c.id) }))}>
|
||||
All remaining
|
||||
<span class="dl-sub">{queueable.length} not yet downloaded</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root { display: flex; flex-direction: column; height: 100vh; width: 100%; background: var(--bg-void); overflow: hidden; position: relative; }
|
||||
.root.overlay-bars .topbar,
|
||||
.root.overlay-bars .bottombar { position: absolute; left: 0; right: 0; z-index: 10; }
|
||||
.root.overlay-bars .topbar { top: 0; }
|
||||
.root.overlay-bars .bottombar { bottom: 0; }
|
||||
.root.overlay-bars .viewer { height: 100%; }
|
||||
.root { position: fixed; inset: 0; background: #000; display: flex; flex-direction: column; z-index: var(--z-reader); transform: translateZ(0); will-change: transform; }
|
||||
.overlay-bars { position: fixed; }
|
||||
.overlay-bars .topbar { position: absolute; top: 0; left: 0; right: 0; z-index: 10; }
|
||||
.overlay-bars .bottombar { position: absolute; bottom: 0; left: 0; right: 0; z-index: 10; }
|
||||
.overlay-bars .viewer { height: 100%; }
|
||||
|
||||
.topbar { display: flex; align-items: center; justify-content: space-between; padding: 0 var(--sp-3); height: 40px; background: var(--bg-void); border-bottom: 1px solid var(--border-dim); flex-shrink: 0; transition: opacity 0.25s ease; gap: var(--sp-2); }
|
||||
.topbar.hidden { opacity: 0; pointer-events: none; }
|
||||
.bottombar.hidden { opacity: 0; pointer-events: none; }
|
||||
.topbar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-1); padding: 0 var(--sp-3); height: 40px; background: var(--bg-void); border-bottom: 1px solid var(--border-dim); flex-shrink: 0; position: relative; z-index: 2; transition: opacity 0.25s ease; }
|
||||
.topbar.hidden, .bottombar.hidden { opacity: 0; pointer-events: none; }
|
||||
|
||||
.topbar-left { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; flex: 1; }
|
||||
.topbar-left { display: flex; align-items: center; gap: var(--sp-1); min-width: 0; flex: 1; overflow: hidden; }
|
||||
.topbar-right { display: flex; align-items: center; gap: var(--sp-1); flex-shrink: 0; }
|
||||
.mode-extras { display: flex; align-items: center; gap: var(--sp-1); min-width: 0; }
|
||||
|
||||
.ch-label { display: flex; align-items: center; gap: var(--sp-1); font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: var(--tracking-wide); min-width: 0; overflow: hidden; }
|
||||
.ch-title { color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; }
|
||||
.ch-sep { color: var(--text-faint); flex-shrink: 0; }
|
||||
.page-label { font-family: var(--font-ui); font-size: var(--text-2xs); color: var(--text-faint); letter-spacing: var(--tracking-wide); white-space: nowrap; flex-shrink: 0; padding-left: var(--sp-1); }
|
||||
|
||||
.icon-btn { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--radius-sm); color: var(--text-faint); background: none; border: none; cursor: pointer; transition: color var(--t-base), background var(--t-base); flex-shrink: 0; }
|
||||
.icon-btn:hover { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.icon-btn:disabled { opacity: 0.3; cursor: default; }
|
||||
.icon-btn { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--radius-sm); color: var(--text-muted); flex-shrink: 0; transition: color var(--t-base), background var(--t-base); }
|
||||
.icon-btn:hover:not(:disabled) { color: var(--text-primary); background: var(--bg-raised); }
|
||||
.icon-btn:disabled { opacity: 0.2; cursor: default; }
|
||||
.icon-btn.active { color: var(--accent-fg); }
|
||||
.marker-btn-has { color: var(--marker-color, var(--accent-fg)) !important; }
|
||||
|
||||
.top-sep { width: 1px; height: 16px; background: var(--border-dim); margin: 0 var(--sp-1); flex-shrink: 0; }
|
||||
.ch-label { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--text-sm); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
||||
.ch-title { color: var(--text-secondary); font-weight: var(--weight-medium); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.ch-sep { color: var(--text-faint); flex-shrink: 0; }
|
||||
.page-label { font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: var(--tracking-wide); flex-shrink: 0; }
|
||||
.top-sep { width: 1px; height: 16px; background: var(--border-dim); flex-shrink: 0; margin: 0 var(--sp-1); }
|
||||
|
||||
.mode-btn { display: flex; align-items: center; gap: 5px; padding: 0 var(--sp-2); height: 28px; border-radius: var(--radius-sm); color: var(--text-faint); background: none; border: none; cursor: pointer; transition: color var(--t-base), background var(--t-base); flex-shrink: 0; }
|
||||
.mode-btn:hover { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.mode-btn.active { color: var(--accent-fg); }
|
||||
.mode-label { font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide); }
|
||||
.mode-btn { display: flex; align-items: center; gap: 4px; padding: 4px var(--sp-2); border-radius: var(--radius-sm); color: var(--text-muted); flex-shrink: 0; font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide); transition: color var(--t-base), background var(--t-base); }
|
||||
.mode-btn:hover { color: var(--text-primary); background: var(--bg-raised); }
|
||||
.mode-btn.active { color: var(--accent-fg); background: var(--accent-muted); }
|
||||
.mode-label { text-transform: capitalize; }
|
||||
|
||||
.mode-extras { display: flex; align-items: center; gap: var(--sp-1); }
|
||||
.toggle-row { display: flex; align-items: center; gap: 4px; cursor: pointer; padding: 0 var(--sp-1); height: 28px; border-radius: var(--radius-sm); transition: background var(--t-base); }
|
||||
.toggle-row:hover { background: var(--bg-raised); }
|
||||
.toggle-label { font-family: var(--font-ui); font-size: var(--text-2xs); color: var(--text-faint); letter-spacing: var(--tracking-wide); user-select: none; }
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
|
||||
|
||||
.dl-wrap { position: relative; }
|
||||
|
||||
.zoom-wrap { position: relative; }
|
||||
.zoom-inline { display: flex; align-items: center; height: 28px; border-radius: var(--radius-sm); border: 1px solid var(--border-dim); overflow: hidden; }
|
||||
.zoom-step-btn { display: flex; align-items: center; justify-content: center; width: 24px; height: 100%; background: none; border: none; cursor: pointer; color: var(--text-faint); transition: color var(--t-base), background var(--t-base); }
|
||||
.zoom-step-btn:hover:not(:disabled) { color: var(--text-muted); background: var(--bg-raised); }
|
||||
.zoom-wrap { position: relative; flex-shrink: 0; }
|
||||
.zoom-inline { display: flex; align-items: center; gap: 1px; background: var(--bg-overlay); border: 1px solid var(--border-base); border-radius: var(--radius-sm); overflow: hidden; }
|
||||
.zoom-step-btn { display: flex; align-items: center; justify-content: center; width: 22px; height: 24px; color: var(--text-muted); transition: color var(--t-base), background var(--t-base); }
|
||||
.zoom-step-btn:hover:not(:disabled) { color: var(--text-primary); background: var(--bg-raised); }
|
||||
.zoom-step-btn:disabled { opacity: 0.25; cursor: default; }
|
||||
.zoom-pct-btn { font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide); color: var(--text-secondary); padding: 0 var(--sp-2); height: 24px; min-width: 38px; text-align: center; transition: color var(--t-base), background var(--t-base); border-left: 1px solid var(--border-dim); border-right: 1px solid var(--border-dim); }
|
||||
.zoom-pct-btn:hover { color: var(--text-primary); background: var(--bg-raised); }
|
||||
@@ -1314,6 +1375,13 @@
|
||||
.marker-cancel-btn { flex: 1; padding: 6px 8px; border-radius: var(--radius-sm); border: 1px solid var(--border-dim); background: none; color: var(--text-faint); font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wide); cursor: pointer; transition: color var(--t-base), border-color var(--t-base); text-align: center; }
|
||||
.marker-cancel-btn:hover { color: var(--text-muted); border-color: var(--border-strong); }
|
||||
|
||||
.wc-wrap { position: relative; flex-shrink: 0; }
|
||||
.wc-dropdown { position: absolute; top: calc(100% + 6px); right: 0; background: var(--bg-raised); border: 1px solid var(--border-base); border-radius: var(--radius-lg); padding: var(--sp-1); display: flex; flex-direction: column; gap: 2px; box-shadow: 0 8px 24px rgba(0,0,0,0.5); z-index: 100; min-width: 148px; animation: scaleIn 0.1s ease both; transform-origin: top right; }
|
||||
.wc-btn { display: flex; align-items: center; gap: var(--sp-2); padding: 6px var(--sp-2); border-radius: var(--radius-sm); background: none; border: none; color: var(--text-muted); font-family: var(--font-ui); font-size: var(--text-xs); letter-spacing: var(--tracking-wide); cursor: pointer; width: 100%; transition: color var(--t-base), background var(--t-base); }
|
||||
.wc-btn svg { flex-shrink: 0; opacity: 0.75; }
|
||||
.wc-btn:hover { color: var(--text-primary); background: var(--bg-overlay); }
|
||||
.wc-close:hover { color: #fff; background: #c0392b; }
|
||||
|
||||
.viewer { flex: 1; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; -webkit-overflow-scrolling: touch; position: relative; }
|
||||
.viewer.strip { justify-content: flex-start; padding: var(--sp-4) 0; }
|
||||
.viewer:focus { outline: none; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { tick } from "svelte";
|
||||
import { X, Book, Image, Sliders, Info, Keyboard, Gear, HardDrives, FolderSimple, Plus, Pencil, Trash, Wrench, PaintBrush, ListChecks, Lock, Eye, EyeSlash, Star, ShieldCheck, Tag, ClockCounterClockwise } from "phosphor-svelte";
|
||||
import ThreeDCard from "../shared/ThreeDCard.svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
import { getVersion } from "@tauri-apps/api/app";
|
||||
@@ -428,6 +429,7 @@
|
||||
return () => document.removeEventListener("mousedown", onSelectOutside);
|
||||
});
|
||||
let splashTriggered = $state(false);
|
||||
let expOpen = $state(false);
|
||||
let showAuthPass = $state(false);
|
||||
let showSocksPass = $state(false);
|
||||
let pinInput = $state(store.settings.appLockPin ?? "");
|
||||
@@ -1186,23 +1188,25 @@
|
||||
<div class="theme-grid">
|
||||
{#each THEMES as theme}
|
||||
{@const active = (store.settings.theme ?? "dark") === theme.id}
|
||||
<button class="theme-card" class:active onclick={() => updateSettings({ theme: theme.id })} title={theme.description}>
|
||||
<div class="theme-preview">
|
||||
<div class="theme-preview-bg" style="background:{theme.swatches[0]}">
|
||||
<div class="theme-preview-sidebar" style="background:{theme.swatches[1]}"></div>
|
||||
<div class="theme-preview-content">
|
||||
<div class="theme-preview-accent" style="background:{theme.swatches[2]}"></div>
|
||||
<div class="theme-preview-text" style="background:{theme.swatches[3]}55"></div>
|
||||
<div class="theme-preview-text" style="background:{theme.swatches[3]}33;width:60%"></div>
|
||||
<div class="theme-card{active ? ' active' : ''}">
|
||||
<button class="theme-card-inner" onclick={() => updateSettings({ theme: theme.id })} title={theme.description}>
|
||||
<div class="theme-preview">
|
||||
<div class="theme-preview-bg" style="background:{theme.swatches[0]}">
|
||||
<div class="theme-preview-sidebar" style="background:{theme.swatches[1]}"></div>
|
||||
<div class="theme-preview-content">
|
||||
<div class="theme-preview-accent" style="background:{theme.swatches[2]}"></div>
|
||||
<div class="theme-preview-text" style="background:{theme.swatches[3]}55"></div>
|
||||
<div class="theme-preview-text" style="background:{theme.swatches[3]}33;width:60%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="theme-card-info">
|
||||
<span class="theme-card-label">{theme.label}</span>
|
||||
<span class="theme-card-desc">{theme.description}</span>
|
||||
</div>
|
||||
{#if active}<span class="theme-card-check">✓</span>{/if}
|
||||
</button>
|
||||
<div class="theme-card-info">
|
||||
<span class="theme-card-label">{theme.label}</span>
|
||||
<span class="theme-card-desc">{theme.description}</span>
|
||||
</div>
|
||||
{#if active}<span class="theme-card-check">✓</span>{/if}
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{#each store.settings.customThemes ?? [] as custom}
|
||||
{@const active = store.settings.theme === custom.id}
|
||||
@@ -2532,6 +2536,32 @@
|
||||
<button class="dev-btn" onclick={triggerSplash} style={splashTriggered ? "background:var(--accent-fg);color:var(--bg-base);border-color:var(--accent-fg)" : ""}>Show</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<button class="exp-disclosure" onclick={() => expOpen = !expOpen} aria-expanded={expOpen}>
|
||||
<span class="exp-disclosure-label">Experimental</span>
|
||||
<svg class="exp-caret" class:open={expOpen} width="10" height="6" viewBox="0 0 10 6"><path d="M0 0l5 6 5-6" fill="currentColor"/></svg>
|
||||
</button>
|
||||
{#if expOpen}
|
||||
<div class="exp-body">
|
||||
<p class="exp-hint">3D tilt cards — hover to preview</p>
|
||||
<div class="exp-demo-wrap">
|
||||
{#each [
|
||||
{ title: "Berserk", sub: "Ch. 372", hue: "265" },
|
||||
{ title: "Vinland Saga", sub: "Ch. 208", hue: "200" },
|
||||
{ title: "Dungeon Meshi", sub: "Ch. 97", hue: "140" },
|
||||
] as card}
|
||||
<ThreeDCard class="exp-demo-card">
|
||||
<div class="exp-demo-cover" style="--hue:{card.hue}"></div>
|
||||
<div class="exp-demo-info">
|
||||
<span class="exp-demo-title">{card.title}</span>
|
||||
<span class="exp-demo-sub">{card.sub}</span>
|
||||
</div>
|
||||
</ThreeDCard>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="section">
|
||||
<p class="section-title">Runtime</p>
|
||||
<div class="dev-grid">
|
||||
@@ -2805,6 +2835,36 @@
|
||||
.dev-key { font-family: var(--font-ui); font-size: var(--text-2xs); letter-spacing: var(--tracking-wider); text-transform: uppercase; color: var(--text-faint); padding: 4px 0; display: flex; align-items: center; }
|
||||
.dev-val { font-family: monospace; font-size: 11px; color: var(--text-secondary); padding: 4px 0; display: flex; align-items: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
|
||||
/* ── Experimental disclosure ─────────────────────────────────────────────── */
|
||||
.exp-disclosure {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
width: 100%; padding: var(--sp-2) var(--sp-3);
|
||||
background: none; border: none; cursor: pointer;
|
||||
color: var(--text-faint); transition: color var(--t-fast);
|
||||
}
|
||||
.exp-disclosure:hover { color: var(--text-secondary); }
|
||||
.exp-disclosure-label { font-family: var(--font-ui); font-size: var(--text-xs); font-weight: var(--weight-medium); letter-spacing: var(--tracking-wider); text-transform: uppercase; }
|
||||
.exp-caret { transition: transform 0.18s ease; flex-shrink: 0; }
|
||||
.exp-caret.open { transform: rotate(180deg); }
|
||||
.exp-body { padding: 0 var(--sp-3) var(--sp-3); display: flex; flex-direction: column; gap: var(--sp-2); }
|
||||
.exp-hint { font-family: var(--font-ui); font-size: var(--text-2xs); color: var(--text-faint); letter-spacing: var(--tracking-wide); }
|
||||
.exp-demo-wrap { display: flex; justify-content: center; gap: var(--sp-3); padding: var(--sp-2) 0; }
|
||||
:global(.exp-demo-card) {
|
||||
width: 90px; border-radius: var(--radius-lg);
|
||||
background: var(--bg-raised); border: 1px solid var(--border-dim);
|
||||
}
|
||||
.exp-demo-cover {
|
||||
height: 120px;
|
||||
background: linear-gradient(160deg,
|
||||
hsl(var(--hue, 220) 30% 18%) 0%,
|
||||
hsl(var(--hue, 220) 40% 28%) 100%
|
||||
);
|
||||
border-bottom: 1px solid var(--border-dim);
|
||||
}
|
||||
.exp-demo-info { padding: 6px 8px; display: flex; flex-direction: column; gap: 2px; }
|
||||
.exp-demo-title { font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.exp-demo-sub { font-family: var(--font-ui); font-size: var(--text-2xs); color: var(--text-faint); letter-spacing: var(--tracking-wide); }
|
||||
|
||||
/* ── Scale / zoom control ────────────────────────────────────────────────── */
|
||||
.scale-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); }
|
||||
.scale-slider { flex: 1; accent-color: var(--accent); }
|
||||
@@ -2831,14 +2891,15 @@
|
||||
|
||||
/* ── Theme grid ──────────────────────────────────────────────────────────── */
|
||||
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(134px, 1fr)); gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); }
|
||||
.theme-card {
|
||||
:global(.theme-card) {
|
||||
background: var(--bg-raised); border: 1px solid var(--border-dim);
|
||||
border-radius: var(--radius-lg); overflow: hidden; cursor: pointer;
|
||||
text-align: left; transition: border-color var(--t-base), box-shadow var(--t-base);
|
||||
position: relative;
|
||||
}
|
||||
.theme-card:hover { border-color: var(--border-strong); box-shadow: 0 2px 12px rgba(0,0,0,0.2); }
|
||||
.theme-card.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
||||
:global(.theme-card:hover) { border-color: var(--border-strong); box-shadow: 0 2px 12px rgba(0,0,0,0.2); }
|
||||
:global(.theme-card.active) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
||||
.theme-card-inner { display: flex; flex-direction: column; width: 100%; background: none; border: none; cursor: pointer; text-align: left; }
|
||||
.theme-preview { height: 68px; overflow: hidden; }
|
||||
.theme-preview-bg { width: 100%; height: 100%; display: flex; }
|
||||
.theme-preview-sidebar { width: 20%; height: 100%; flex-shrink: 0; }
|
||||
@@ -2868,7 +2929,7 @@
|
||||
.new-theme-card { display: flex; flex-direction: column; border-style: dashed !important; border-color: var(--border-base) !important; background: transparent !important; transition: border-color var(--t-base) !important, background var(--t-base) !important; }
|
||||
.new-theme-card:hover { border-color: var(--accent-dim) !important; background: var(--accent-muted) !important; }
|
||||
.new-theme-icon { height: 68px; display: flex; align-items: center; justify-content: center; color: var(--text-faint); transition: color var(--t-base); }
|
||||
.new-theme-card:hover .new-theme-icon { color: var(--accent-fg); }
|
||||
:global(.new-theme-card:hover) .new-theme-icon { color: var(--accent-fg); }
|
||||
|
||||
/* ── Keybinds ────────────────────────────────────────────────────────────── */
|
||||
.kb-hint { font-family: var(--font-ui); font-size: var(--text-xs); color: var(--text-faint); letter-spacing: var(--tracking-wide); padding: 0 var(--sp-3) var(--sp-3); }
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
<script lang="ts">
|
||||
import { type Snippet } from "svelte";
|
||||
|
||||
interface Props {
|
||||
children: Snippet;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
let { children, class: cls = "" }: Props = $props();
|
||||
</script>
|
||||
|
||||
<!--
|
||||
Structure mirrors daisyUI hover-3d:
|
||||
- :first-child (.hover-3d-content) → the card, gets rotate3d + scale
|
||||
- :nth-child(2..9) → 8 invisible zone divs occupying the 3×3 grid
|
||||
The wrapper IS the inline-grid, zones sit on top via z-index,
|
||||
tilt is driven purely by CSS :has() — zero JS.
|
||||
-->
|
||||
<div class="hover-3d {cls}">
|
||||
<div class="hover-3d-content">
|
||||
{@render children()}
|
||||
</div>
|
||||
<!-- 8 zones: TL TC TR ML MR BL BC BR (no centre) -->
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hover-3d {
|
||||
display: inline-grid;
|
||||
perspective: 75rem;
|
||||
--transform: 0, 0;
|
||||
--shine: 100% 100%;
|
||||
--shadow: 0rem 0rem 0rem;
|
||||
--ease-out: linear(0, 0.931 13.8%, 1.196 21.4%, 1.343 29.8%, 1.378 36%, 1.365 43.2%, 1.059 78%, 1);
|
||||
--ease-hover: linear(0, 0.708 15.2%, 0.927 23.6%, 1.067 33%, 1.12 41%, 1.13 50.2%, 1.019 83.2%, 1);
|
||||
filter:
|
||||
drop-shadow(var(--shadow) 0.1rem #00000020)
|
||||
drop-shadow(var(--shadow) 0.2rem #00000015)
|
||||
drop-shadow(var(--shadow) 0.3rem #00000010);
|
||||
transition: filter ease-out 400ms;
|
||||
}
|
||||
|
||||
/* Zone divs sit above the card content */
|
||||
.hover-3d > :nth-child(n + 2) {
|
||||
isolation: isolate;
|
||||
z-index: 1;
|
||||
scale: 1.2;
|
||||
}
|
||||
|
||||
/* 3×3 grid positions for the 8 zones */
|
||||
.hover-3d > :nth-child(2) { grid-area: 1/1/2/2; }
|
||||
.hover-3d > :nth-child(3) { grid-area: 1/2/2/3; }
|
||||
.hover-3d > :nth-child(4) { grid-area: 1/3/2/4; }
|
||||
.hover-3d > :nth-child(5) { grid-area: 2/1/3/2; }
|
||||
.hover-3d > :nth-child(6) { grid-area: 2/3/3/4; }
|
||||
.hover-3d > :nth-child(7) { grid-area: 3/1/4/2; }
|
||||
.hover-3d > :nth-child(8) { grid-area: 3/2/4/3; }
|
||||
.hover-3d > :nth-child(9) { grid-area: 3/3/4/4; }
|
||||
|
||||
/* The card itself */
|
||||
.hover-3d-content {
|
||||
grid-area: 1/1/4/4;
|
||||
overflow: hidden;
|
||||
border-radius: inherit;
|
||||
position: relative;
|
||||
transform: rotate3d(var(--transform), 0, 10deg);
|
||||
transition:
|
||||
transform var(--ease-out) 500ms,
|
||||
scale var(--ease-out) 500ms,
|
||||
outline-color ease-out 500ms;
|
||||
outline: 0.5px solid transparent;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* Shine overlay */
|
||||
.hover-3d-content::before {
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
filter: blur(0.75rem);
|
||||
background-image: radial-gradient(circle at 50%, rgba(255,255,255,0.18) 10%, transparent 50%);
|
||||
translate: var(--shine);
|
||||
transition:
|
||||
translate ease-out 400ms,
|
||||
opacity ease-out 400ms;
|
||||
}
|
||||
|
||||
/* On hover: snappier ease, scale up, show shine + outline */
|
||||
.hover-3d:hover {
|
||||
--ease-out: var(--ease-hover);
|
||||
}
|
||||
.hover-3d:hover > .hover-3d-content {
|
||||
scale: 1.05;
|
||||
outline-color: rgba(255,255,255,0.07);
|
||||
}
|
||||
.hover-3d:hover > .hover-3d-content::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Zone → rotate3d + shine + shadow mappings */
|
||||
.hover-3d:has(> :nth-child(2):hover) { --transform: -1, 1; --shine: 0% 0%; --shadow: -0.5rem -0.5rem; }
|
||||
.hover-3d:has(> :nth-child(3):hover) { --transform: -1, 0; --shine: 100% 0%; --shadow: 0rem -0.5rem; }
|
||||
.hover-3d:has(> :nth-child(4):hover) { --transform: -1, -1; --shine: 200% 0%; --shadow: 0.5rem -0.5rem; }
|
||||
.hover-3d:has(> :nth-child(5):hover) { --transform: 0, 1; --shine: 0% 100%; --shadow: -0.5rem 0rem; }
|
||||
.hover-3d:has(> :nth-child(6):hover) { --transform: 0, -1; --shine: 200% 100%; --shadow: 0.5rem 0rem; }
|
||||
.hover-3d:has(> :nth-child(7):hover) { --transform: 1, 1; --shine: 0% 200%; --shadow: -0.5rem 0.5rem; }
|
||||
.hover-3d:has(> :nth-child(8):hover) { --transform: 1, 0; --shine: 100% 200%; --shadow: 0rem 0.5rem; }
|
||||
.hover-3d:has(> :nth-child(9):hover) { --transform: 1, -1; --shine: 200% 200%; --shadow: 0.5rem 0.5rem; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user