Web UI: move downloads from bottom bar to ⬇ modal
The fixed bottom #jobs bar ate up to 40vh of screen real estate even when only one job was running, and on mobile it competed with the URL input. Replace it with a ⬇ button in the header that: - shows a badge with active+queued count (turns accent-colored when >0) - opens a modal listing all jobs when clicked (or with the 'd' shortcut) - repaints live from the same WS/polling snapshot that drove the old bar The footer URL input + quality picker stay where they are — that's the entry point, not the status display. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2e98d8553f
commit
0d7ae83873
3 changed files with 131 additions and 68 deletions
123
Cargo.lock
generated
123
Cargo.lock
generated
|
|
@ -457,9 +457,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
|
|
@ -627,9 +627,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.20.2"
|
version = "3.20.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
|
|
@ -1001,9 +1001,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -1697,9 +1697,9 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
|
@ -2013,9 +2013,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.98"
|
version = "0.3.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
|
@ -2120,9 +2120,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.29"
|
version = "0.4.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mac-notification-sys"
|
name = "mac-notification-sys"
|
||||||
|
|
@ -2153,9 +2153,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
|
|
@ -2332,9 +2332,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
|
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
|
|
@ -2658,9 +2658,9 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.54"
|
version = "0.3.55"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329"
|
checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"libredox",
|
"libredox",
|
||||||
|
|
@ -2739,18 +2739,18 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.12"
|
version = "1.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9"
|
checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-internal",
|
"pin-project-internal",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-internal"
|
name = "pin-project-internal"
|
||||||
version = "1.1.12"
|
version = "1.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389"
|
checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
@ -3259,9 +3259,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.149"
|
version = "1.0.150"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|
@ -3732,21 +3732,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.2"
|
version = "0.8.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
|
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime 0.6.3",
|
"toml_datetime 0.6.11",
|
||||||
"toml_edit 0.20.2",
|
"toml_edit 0.22.27",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.3"
|
version = "0.6.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
@ -3762,15 +3762,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.20.2"
|
version = "0.22.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime 0.6.3",
|
"toml_datetime 0.6.11",
|
||||||
"winnow 0.5.40",
|
"toml_write",
|
||||||
|
"winnow 0.7.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3782,7 +3783,7 @@ dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime 1.1.1+spec-1.1.0",
|
"toml_datetime 1.1.1+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3791,9 +3792,15 @@ version = "1.1.2+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
@ -4053,9 +4060,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.121"
|
version = "0.2.122"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
|
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
@ -4066,9 +4073,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-futures"
|
name = "wasm-bindgen-futures"
|
||||||
version = "0.4.71"
|
version = "0.4.72"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
|
checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
@ -4076,9 +4083,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.121"
|
version = "0.2.122"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
|
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
|
|
@ -4086,9 +4093,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro-support"
|
name = "wasm-bindgen-macro-support"
|
||||||
version = "0.2.121"
|
version = "0.2.122"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
|
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
|
@ -4099,9 +4106,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-shared"
|
name = "wasm-bindgen-shared"
|
||||||
version = "0.2.121"
|
version = "0.2.122"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
|
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
@ -4277,9 +4284,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.98"
|
version = "0.3.99"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa"
|
checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|
@ -4877,18 +4884,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.40"
|
version = "0.7.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "1.0.2"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
|
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
@ -5180,7 +5187,7 @@ dependencies = [
|
||||||
"uds_windows",
|
"uds_windows",
|
||||||
"uuid",
|
"uuid",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
"zbus_macros 5.15.0",
|
"zbus_macros 5.15.0",
|
||||||
"zbus_names 4.3.2",
|
"zbus_names 4.3.2",
|
||||||
"zvariant 5.11.0",
|
"zvariant 5.11.0",
|
||||||
|
|
@ -5256,7 +5263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
|
checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
"zvariant 5.11.0",
|
"zvariant 5.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -5295,9 +5302,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom"
|
name = "zerofrom"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
|
checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerofrom-derive",
|
"zerofrom-derive",
|
||||||
]
|
]
|
||||||
|
|
@ -5391,7 +5398,7 @@ dependencies = [
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"serde",
|
"serde",
|
||||||
"url",
|
"url",
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
"zvariant_derive 5.11.0",
|
"zvariant_derive 5.11.0",
|
||||||
"zvariant_utils 3.3.1",
|
"zvariant_utils 3.3.1",
|
||||||
]
|
]
|
||||||
|
|
@ -5443,5 +5450,5 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
"winnow 1.0.2",
|
"winnow 1.0.3",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
2
PKGBUILD
2
PKGBUILD
|
|
@ -19,7 +19,7 @@ optdepends=(
|
||||||
)
|
)
|
||||||
makedepends=('rust' 'cargo')
|
makedepends=('rust' 'cargo')
|
||||||
options=('!lto') # rusqlite bundled sqlite cannot be LTO-linked with rust-lld
|
options=('!lto') # rusqlite bundled sqlite cannot be LTO-linked with rust-lld
|
||||||
source=("git+https://codeberg.org/anassaeneroi/yt-offline.git")
|
source=("git+https://codeberg.org/anassaeneroi/yt-offline.git#branch=main")
|
||||||
sha256sums=('SKIP')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,14 @@
|
||||||
.meta-tab{padding:4px 10px;cursor:pointer;font-size:12px;border:1px solid transparent;border-bottom:none;border-radius:4px 4px 0 0}
|
.meta-tab{padding:4px 10px;cursor:pointer;font-size:12px;border:1px solid transparent;border-bottom:none;border-radius:4px 4px 0 0}
|
||||||
.meta-tab.active{background:var(--card);border-color:var(--border)}
|
.meta-tab.active{background:var(--card);border-color:var(--border)}
|
||||||
.meta-raw{font-family:monospace;font-size:11px;background:var(--bg);padding:10px;border-radius:4px;white-space:pre;overflow:auto;max-height:60vh}
|
.meta-raw{font-family:monospace;font-size:11px;background:var(--bg);padding:10px;border-radius:4px;white-space:pre;overflow:auto;max-height:60vh}
|
||||||
#jobs{background:var(--panel);border-top:1px solid var(--border);flex-shrink:0;max-height:40vh;overflow-y:auto}
|
/* Jobs render inside the Downloads modal now. The dl-btn badge
|
||||||
|
on the header shows the active count so users still get an
|
||||||
|
at-a-glance status without the old fixed bottom bar eating
|
||||||
|
screen real estate. */
|
||||||
|
#dl-btn{position:relative}
|
||||||
|
#dl-btn.has-active{color:var(--accent)}
|
||||||
|
#dl-badge{position:absolute;top:-4px;right:-4px;background:var(--accent);color:#000;font-size:10px;font-weight:700;border-radius:9px;padding:1px 5px;min-width:16px;text-align:center;line-height:1.2;display:none}
|
||||||
|
#dl-btn.has-active #dl-badge{display:inline-block}
|
||||||
.job{display:flex;align-items:center;gap:8px;padding:5px 14px;font-size:12px;border-bottom:1px solid var(--border);flex-wrap:wrap;min-width:0}
|
.job{display:flex;align-items:center;gap:8px;padding:5px 14px;font-size:12px;border-bottom:1px solid var(--border);flex-wrap:wrap;min-width:0}
|
||||||
.badge{font-weight:700;min-width:48px;flex-shrink:0}
|
.badge{font-weight:700;min-width:48px;flex-shrink:0}
|
||||||
.badge.running{color:#facc15}.badge.done{color:#4ade80}.badge.failed{color:#f87171}
|
.badge.running{color:#facc15}.badge.done{color:#4ade80}.badge.failed{color:#f87171}
|
||||||
|
|
@ -164,6 +171,7 @@
|
||||||
<span id="hdr-stats"></span>
|
<span id="hdr-stats"></span>
|
||||||
<button onclick="shufflePlay()" title="Play a random unwatched video">🎲</button>
|
<button onclick="shufflePlay()" title="Play a random unwatched video">🎲</button>
|
||||||
<button onclick="rescan()" title="Rescan library">⟳</button>
|
<button onclick="rescan()" title="Rescan library">⟳</button>
|
||||||
|
<button id="dl-btn" onclick="openDownloads()" title="Downloads">⬇<span id="dl-badge">0</span></button>
|
||||||
<button onclick="openStats()" title="Library statistics">📊</button>
|
<button onclick="openStats()" title="Library statistics">📊</button>
|
||||||
<button onclick="openMaintenance()" title="Library health">🩺</button>
|
<button onclick="openMaintenance()" title="Library health">🩺</button>
|
||||||
<button onclick="showShortcutsHelp()" title="Keyboard shortcuts (?)">?</button>
|
<button onclick="showShortcutsHelp()" title="Keyboard shortcuts (?)">?</button>
|
||||||
|
|
@ -189,7 +197,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<div id="details"></div>
|
<div id="details"></div>
|
||||||
<div id="jobs"></div>
|
|
||||||
<footer>
|
<footer>
|
||||||
<input type="url" id="dl-url" placeholder="YouTube URL…" oninput="refreshTwitchClipsVisibility()" onkeydown="if(event.key==='Enter')previewDownload()">
|
<input type="url" id="dl-url" placeholder="YouTube URL…" oninput="refreshTwitchClipsVisibility()" onkeydown="if(event.key==='Enter')previewDownload()">
|
||||||
<button class="primary" onclick="previewDownload()">⬇ Download</button>
|
<button class="primary" onclick="previewDownload()">⬇ Download</button>
|
||||||
|
|
@ -1282,7 +1289,7 @@ function fmtCountdown(secs){
|
||||||
async function updateYtdlp(btn){
|
async function updateYtdlp(btn){
|
||||||
btn.disabled=true;
|
btn.disabled=true;
|
||||||
const st=document.getElementById('ytdlp-status');
|
const st=document.getElementById('ytdlp-status');
|
||||||
st.textContent='Started — see Downloads bar';
|
st.textContent='Started — click ⬇ in the header to view progress';
|
||||||
try{
|
try{
|
||||||
const r=await fetch('/api/ytdlp/update',{method:'POST'});
|
const r=await fetch('/api/ytdlp/update',{method:'POST'});
|
||||||
const t=await r.text();
|
const t=await r.text();
|
||||||
|
|
@ -1505,17 +1512,38 @@ async function repairAll(btn){
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Jobs ───────────────────────────────────────────────────────── */
|
/* ── Jobs ───────────────────────────────────────────────────────── */
|
||||||
|
// Jobs now live behind the ⬇ Downloads modal instead of a fixed bottom
|
||||||
|
// bar. This function:
|
||||||
|
// 1. Updates the header badge (active + queued count) on every snapshot
|
||||||
|
// 2. If the modal is open, repaints its body so progress bars stay live
|
||||||
|
// The modal is identified by its data-downloads-modal attribute so we can
|
||||||
|
// reach it without scanning every .modal-bg in the DOM.
|
||||||
function renderJobs(jobs,queued,maxConcurrent){
|
function renderJobs(jobs,queued,maxConcurrent){
|
||||||
const el=document.getElementById('jobs');
|
jobs=jobs||[];queued=queued||[];
|
||||||
if(!jobs.length&&!queued?.length){el.innerHTML='';return}
|
// Badge: active jobs (running) + queued. Finished/failed don't bump the
|
||||||
|
// badge — they're informational, not in-progress.
|
||||||
|
const activeCount=jobs.filter(j=>j.state==='running').length+queued.length;
|
||||||
|
const btn=document.getElementById('dl-btn');
|
||||||
|
const badge=document.getElementById('dl-badge');
|
||||||
|
if(btn&&badge){
|
||||||
|
badge.textContent=String(activeCount);
|
||||||
|
btn.classList.toggle('has-active',activeCount>0);
|
||||||
|
}
|
||||||
|
// If the modal isn't open we're done.
|
||||||
|
const body=document.getElementById('downloads-body');
|
||||||
|
if(!body)return;
|
||||||
|
if(!jobs.length&&!queued.length){
|
||||||
|
body.innerHTML=`<div class="empty">No active or recent downloads.</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const fin=jobs.some(j=>j.state!=='running');
|
const fin=jobs.some(j=>j.state!=='running');
|
||||||
const hdr=fin?`<div style="padding:4px 14px;display:flex;justify-content:flex-end;border-bottom:1px solid var(--border)"><button onclick="clearFinishedJobs()" style="font-size:11px;padding:2px 8px">✕ Clear finished</button></div>`:'';
|
const hdr=fin?`<div style="padding:4px 14px;display:flex;justify-content:flex-end;border-bottom:1px solid var(--border)"><button onclick="clearFinishedJobs()" style="font-size:11px;padding:2px 8px">✕ Clear finished</button></div>`:'';
|
||||||
const queuedHtml=(queued&&queued.length)?queued.map(q=>`<div class="job">
|
const queuedHtml=queued.map(q=>`<div class="job">
|
||||||
<span class="badge" style="color:var(--muted)">queued</span>
|
<span class="badge" style="color:var(--muted)">queued</span>
|
||||||
<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)">${esc(q.label)} — ${esc(q.url)}</span>
|
<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)">${esc(q.label)} — ${esc(q.url)}</span>
|
||||||
</div>`).join(''):'';
|
</div>`).join('');
|
||||||
const limitNote=maxConcurrent>0?`<div style="font-size:11px;color:var(--muted);padding:2px 14px">max ${maxConcurrent} concurrent</div>`:'';
|
const limitNote=maxConcurrent>0?`<div style="font-size:11px;color:var(--muted);padding:6px 14px">max ${maxConcurrent} concurrent</div>`:'';
|
||||||
el.innerHTML=hdr+jobs.map((j,i)=>{
|
body.innerHTML=hdr+jobs.map((j,i)=>{
|
||||||
const dismiss=j.state!=='running'?`<button onclick="removeJob(${i})" style="font-size:11px;padding:1px 6px">✕</button>`:'';
|
const dismiss=j.state!=='running'?`<button onclick="removeJob(${i})" style="font-size:11px;padding:1px 6px">✕</button>`:'';
|
||||||
return `<div class="job">
|
return `<div class="job">
|
||||||
<span class="badge ${j.state}">${j.state}</span>
|
<span class="badge ${j.state}">${j.state}</span>
|
||||||
|
|
@ -1526,6 +1554,29 @@ function renderJobs(jobs,queued,maxConcurrent){
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('')+queuedHtml+limitNote;
|
}).join('')+queuedHtml+limitNote;
|
||||||
}
|
}
|
||||||
|
// Open the Downloads modal. If it's already open, do nothing (clicking the
|
||||||
|
// ⬇ button again would otherwise stack duplicate modals).
|
||||||
|
async function openDownloads(){
|
||||||
|
if(document.querySelector('[data-downloads-modal]'))return;
|
||||||
|
const bg=document.createElement('div');
|
||||||
|
bg.className='modal-bg';
|
||||||
|
bg.setAttribute('data-downloads-modal','1');
|
||||||
|
bg.onclick=e=>{if(e.target===bg)bg.remove()};
|
||||||
|
bg.innerHTML=`<div class="modal" style="max-width:720px;width:100%">
|
||||||
|
<div class="modal-hdr"><h2>⬇ Downloads</h2><button onclick="this.closest('.modal-bg').remove()">✕</button></div>
|
||||||
|
<div id="downloads-body" style="overflow:auto;max-height:80vh;min-height:120px"><em style="color:var(--muted);padding:14px;display:block">Loading…</em></div>
|
||||||
|
</div>`;
|
||||||
|
document.body.appendChild(bg);
|
||||||
|
// Repaint immediately from the latest snapshot if we have one — saves a
|
||||||
|
// round trip. Falls through to /api/progress otherwise.
|
||||||
|
try{
|
||||||
|
const d=await(await api('/api/progress')).json();
|
||||||
|
renderJobs(d.jobs,d.queued,d.max_concurrent);
|
||||||
|
}catch(e){
|
||||||
|
document.getElementById('downloads-body').innerHTML=
|
||||||
|
`<div style="color:#f87171;padding:14px">Failed to load: ${esc(e.message)}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
async function clearFinishedJobs(){try{await api('/api/jobs/clear',{method:'POST'});await pollProgress()}catch(e){setStatus('Error: '+e.message)}}
|
async function clearFinishedJobs(){try{await api('/api/jobs/clear',{method:'POST'});await pollProgress()}catch(e){setStatus('Error: '+e.message)}}
|
||||||
async function removeJob(idx){try{await api('/api/jobs/'+idx,{method:'DELETE'});await pollProgress()}catch(e){setStatus('Error: '+e.message)}}
|
async function removeJob(idx){try{await api('/api/jobs/'+idx,{method:'DELETE'});await pollProgress()}catch(e){setStatus('Error: '+e.message)}}
|
||||||
|
|
||||||
|
|
@ -1608,6 +1659,10 @@ document.addEventListener('keydown',(e)=>{
|
||||||
rescan();
|
rescan();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
case 'd':
|
||||||
|
openDownloads();
|
||||||
|
e.preventDefault();
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
showShortcutsHelp();
|
showShortcutsHelp();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -1624,6 +1679,7 @@ function showShortcutsHelp(){
|
||||||
<table><tbody>
|
<table><tbody>
|
||||||
${row('/','Focus search')}
|
${row('/','Focus search')}
|
||||||
${row('r','Rescan library')}
|
${row('r','Rescan library')}
|
||||||
|
${row('d','Open downloads')}
|
||||||
${row('Esc','Close modal / cancel')}
|
${row('Esc','Close modal / cancel')}
|
||||||
${row('?','This help')}
|
${row('?','This help')}
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue