Apply VibeTorrent Glassmorphism Edition theme
Some checks failed
Build MIPS Binary / build (push) Failing after 3s

This commit is contained in:
spinline
2026-02-08 22:13:54 +03:00
parent d3792e78e0
commit 2d5c2325df
8 changed files with 285 additions and 80 deletions

110
Cargo.lock generated
View File

@@ -566,6 +566,15 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "codee"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d3ad3122b0001c7f140cf4d605ef9a9e2c24d96ab0b4fb4347b76de2425f445"
dependencies = [
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "collection_literals" name = "collection_literals"
version = "1.0.3" version = "1.0.3"
@@ -790,6 +799,41 @@ version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8" checksum = "9b10589d1a5e400d61f9f38f12f884cfd080ff345de8f17efda36fe0e4a02aa8"
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.114",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.114",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "5.5.3" version = "5.5.3"
@@ -823,6 +867,18 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
[[package]]
name = "default-struct-builder"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0df63c21a4383f94bd5388564829423f35c316aed85dc4f8427aded372c7c0d"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]] [[package]]
name = "der" name = "der"
version = "0.4.5" version = "0.4.5"
@@ -1144,7 +1200,9 @@ dependencies = [
name = "frontend" name = "frontend"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64 0.22.1",
"chrono", "chrono",
"codee",
"console_error_panic_hook", "console_error_panic_hook",
"console_log", "console_log",
"futures", "futures",
@@ -1152,9 +1210,11 @@ dependencies = [
"gloo-timers", "gloo-timers",
"js-sys", "js-sys",
"leptos", "leptos",
"leptos-use",
"leptos_router", "leptos_router",
"log", "log",
"serde", "serde",
"serde-wasm-bindgen",
"serde_json", "serde_json",
"shared", "shared",
"tailwind_fuse", "tailwind_fuse",
@@ -1819,6 +1879,12 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "1.1.0" version = "1.1.0"
@@ -1986,6 +2052,30 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "leptos-use"
version = "0.13.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "789bf9f4337e6ebd8f1b407e3f762fdc538d48dc145f9d1dce2338014b38f4dd"
dependencies = [
"cfg-if",
"chrono",
"codee",
"cookie",
"default-struct-builder",
"futures-util",
"gloo-timers",
"js-sys",
"lazy_static",
"leptos",
"paste",
"thiserror 1.0.69",
"unic-langid",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "leptos_config" name = "leptos_config"
version = "0.6.15" version = "0.6.15"
@@ -3934,6 +4024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"serde_core",
"zerovec", "zerovec",
] ]
@@ -4298,6 +4389,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unic-langid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05"
dependencies = [
"unic-langid-impl",
]
[[package]]
name = "unic-langid-impl"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658"
dependencies = [
"tinystr",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.9.0" version = "2.9.0"
@@ -5055,6 +5164,7 @@ version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [ dependencies = [
"serde",
"yoke", "yoke",
"zerofrom", "zerofrom",
"zerovec-derive", "zerovec-derive",

View File

@@ -20,6 +20,11 @@
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" /> <link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" /> <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Trunk Assets --> <!-- Trunk Assets -->
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="0" /> <link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="0" />
<link data-trunk rel="css" href="public/tailwind.css" /> <link data-trunk rel="css" href="public/tailwind.css" />

View File

@@ -3,14 +3,42 @@
@plugin "daisyui" { @plugin "daisyui" {
themes: themes:
light, dark, dim, nord, cupcake, dracula, cyberpunk, emerald, sunset, vibeglass, dark;
abyss;
} }
@layer base { @layer base {
html {
font-family: 'Space Grotesk', sans-serif;
}
html, html,
body { body {
@apply min-h-dvh w-full overflow-hidden bg-base-100 text-base-content overscroll-y-none; @apply min-h-dvh w-full overflow-hidden bg-[#0f172a] text-base-content overscroll-y-none;
background-image:
radial-gradient(circle at 15% 50%, rgba(244, 157, 37, 0.08), transparent 25%),
radial-gradient(circle at 85% 30%, rgba(59, 130, 246, 0.08), transparent 25%);
background-attachment: fixed;
}
/* Glassmorphism Utilities */
.glass-panel {
@apply bg-gray-800/40 backdrop-blur-xl border border-white/10 shadow-xl;
}
.glass-sidebar {
@apply bg-gray-900/60 backdrop-blur-xl border-r border-white/5;
}
.glass-header {
@apply bg-gray-900/40 backdrop-blur-md border-b border-white/5;
}
.glass-input {
@apply bg-gray-700/30 border border-white/10 text-white placeholder-gray-400 focus:border-primary focus:ring-1 focus:ring-primary transition-all backdrop-blur-sm;
}
.glass-card {
@apply bg-gray-800/30 backdrop-blur-md border border-white/5 shadow-lg hover:bg-gray-800/40 transition-colors;
} }
} }
@@ -30,3 +58,22 @@
:focus { :focus {
outline: none !important; outline: none !important;
} }
/* Scrollbar styling for glass theme */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.2);
}

View File

@@ -6,13 +6,13 @@ use crate::components::layout::toolbar::Toolbar;
#[component] #[component]
pub fn Protected(children: Children) -> impl IntoView { pub fn Protected(children: Children) -> impl IntoView {
view! { view! {
<div class="drawer lg:drawer-open h-full w-full"> <div class="drawer lg:drawer-open h-full w-full bg-transparent">
<input id="my-drawer" type="checkbox" class="drawer-toggle" /> <input id="my-drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content flex flex-col h-full overflow-hidden bg-base-100 text-base-content text-sm select-none"> <div class="drawer-content flex flex-col h-full overflow-hidden text-base-content text-sm select-none">
<Toolbar /> <Toolbar />
<main class="flex-1 flex flex-col min-w-0 bg-base-100 overflow-hidden pb-8"> <main class="flex-1 flex flex-col min-w-0 overflow-hidden pb-8">
{children()} {children()}
</main> </main>
@@ -21,7 +21,7 @@ pub fn Protected(children: Children) -> impl IntoView {
<div class="drawer-side z-40 transition-none duration-0"> <div class="drawer-side z-40 transition-none duration-0">
<label for="my-drawer" aria-label="close sidebar" class="drawer-overlay transition-none duration-0"></label> <label for="my-drawer" aria-label="close sidebar" class="drawer-overlay transition-none duration-0"></label>
<div class="menu p-0 min-h-full bg-base-200 text-base-content border-r border-base-300 transition-none duration-0"> <div class="menu p-0 min-h-full bg-transparent border-r border-white/5 transition-none duration-0">
<Sidebar /> <Sidebar />
</div> </div>
</div> </div>

View File

@@ -104,17 +104,17 @@ pub fn Sidebar() -> impl IntoView {
view! { view! {
<div class="w-64 min-h-[100dvh] flex flex-col bg-base-200 border-r border-base-300 pb-8" style="padding-top: env(safe-area-inset-top);"> <div class="w-64 min-h-[100dvh] flex flex-col glass-sidebar pb-8" style="padding-top: env(safe-area-inset-top);">
<div class="p-2 flex-1 overflow-y-auto"> <div class="p-2 flex-1 overflow-y-auto">
<ul class="menu w-full rounded-box gap-1"> <ul class="menu w-full gap-1">
<li class="menu-title text-primary uppercase font-bold px-4">"Filters"</li> <li class="menu-title text-primary uppercase font-bold px-4 tracking-wider text-xs opacity-80">"Filters"</li>
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::All))} on:click=move |_| set_filter(crate::store::FilterStatus::All)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::All))} on:click=move |_| set_filter(crate::store::FilterStatus::All)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -124,7 +124,7 @@ pub fn Sidebar() -> impl IntoView {
"All" "All"
<span class="badge badge-sm badge-ghost ml-auto">{total_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{total_count}</span>
</button> </button>
@@ -132,7 +132,7 @@ pub fn Sidebar() -> impl IntoView {
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::Downloading))} on:click=move |_| set_filter(crate::store::FilterStatus::Downloading)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::Downloading))} on:click=move |_| set_filter(crate::store::FilterStatus::Downloading)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -142,7 +142,7 @@ pub fn Sidebar() -> impl IntoView {
"Downloading" "Downloading"
<span class="badge badge-sm badge-ghost ml-auto">{downloading_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{downloading_count}</span>
</button> </button>
@@ -150,7 +150,7 @@ pub fn Sidebar() -> impl IntoView {
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::Seeding))} on:click=move |_| set_filter(crate::store::FilterStatus::Seeding)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::Seeding))} on:click=move |_| set_filter(crate::store::FilterStatus::Seeding)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -160,7 +160,7 @@ pub fn Sidebar() -> impl IntoView {
"Seeding" "Seeding"
<span class="badge badge-sm badge-ghost ml-auto">{seeding_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{seeding_count}</span>
</button> </button>
@@ -168,7 +168,7 @@ pub fn Sidebar() -> impl IntoView {
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::Completed))} on:click=move |_| set_filter(crate::store::FilterStatus::Completed)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::Completed))} on:click=move |_| set_filter(crate::store::FilterStatus::Completed)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -178,7 +178,7 @@ pub fn Sidebar() -> impl IntoView {
"Completed" "Completed"
<span class="badge badge-sm badge-ghost ml-auto">{completed_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{completed_count}</span>
</button> </button>
@@ -186,7 +186,7 @@ pub fn Sidebar() -> impl IntoView {
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::Paused))} on:click=move |_| set_filter(crate::store::FilterStatus::Paused)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::Paused))} on:click=move |_| set_filter(crate::store::FilterStatus::Paused)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -196,7 +196,7 @@ pub fn Sidebar() -> impl IntoView {
"Paused" "Paused"
<span class="badge badge-sm badge-ghost ml-auto">{paused_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{paused_count}</span>
</button> </button>
@@ -204,7 +204,7 @@ pub fn Sidebar() -> impl IntoView {
<li> <li>
<button class={move || format!("cursor-pointer {}", filter_class(crate::store::FilterStatus::Inactive))} on:click=move |_| set_filter(crate::store::FilterStatus::Inactive)> <button class={move || format!("cursor-pointer hover:bg-white/10 {}", filter_class(crate::store::FilterStatus::Inactive))} on:click=move |_| set_filter(crate::store::FilterStatus::Inactive)>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
@@ -214,7 +214,7 @@ pub fn Sidebar() -> impl IntoView {
"Inactive" "Inactive"
<span class="badge badge-sm badge-ghost ml-auto">{inactive_count}</span> <span class="badge badge-sm badge-ghost ml-auto bg-white/10 border-0">{inactive_count}</span>
</button> </button>
@@ -226,13 +226,13 @@ pub fn Sidebar() -> impl IntoView {
<div class="p-4 border-t border-base-300 bg-base-200/50"> <div class="p-4 border-t border-white/5 bg-black/20 backdrop-blur-sm">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="avatar"> <div class="avatar">
<div class="w-8 rounded-full bg-neutral text-neutral-content ring ring-primary ring-offset-base-100 ring-offset-1"> <div class="w-8 rounded-full bg-primary/20 text-primary ring ring-primary ring-offset-base-100 ring-offset-1 ring-opacity-50">
<span class="text-sm font-bold flex items-center justify-center h-full">{first_letter}</span> <span class="text-sm font-bold flex items-center justify-center h-full">{first_letter}</span>
@@ -242,9 +242,9 @@ pub fn Sidebar() -> impl IntoView {
<div class="flex-1 overflow-hidden"> <div class="flex-1 overflow-hidden">
<div class="font-bold text-sm truncate">{username}</div> <div class="font-bold text-sm truncate text-white">{username}</div>
<div class="text-[10px] text-base-content/60 truncate">"Online"</div> <div class="text-[10px] text-white/50 truncate">"Online"</div>
</div> </div>

View File

@@ -6,7 +6,7 @@ pub fn Toolbar() -> impl IntoView {
let store = use_context::<crate::store::TorrentStore>().expect("store not provided"); let store = use_context::<crate::store::TorrentStore>().expect("store not provided");
view! { view! {
<div class="navbar min-h-14 h-auto bg-base-100 p-0" style="padding-top: env(safe-area-inset-top);"> <div class="navbar glass-header min-h-14 h-auto p-0" style="padding-top: env(safe-area-inset-top);">
<div class="navbar-start gap-4 px-4"> <div class="navbar-start gap-4 px-4">
<label for="my-drawer" class="btn btn-square btn-ghost lg:hidden drawer-button"> <label for="my-drawer" class="btn btn-square btn-ghost lg:hidden drawer-button">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-5 h-5 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-5 h-5 stroke-current"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
@@ -14,7 +14,7 @@ pub fn Toolbar() -> impl IntoView {
<div class="flex gap-2"> <div class="flex gap-2">
<button <button
class="btn btn-sm btn-primary gap-2 font-normal" class="btn btn-sm btn-primary gap-2 font-normal border-0 bg-primary/80 hover:bg-primary backdrop-blur text-white shadow-lg shadow-primary/20"
title="Add Magnet Link" title="Add Magnet Link"
on:click=move |_| set_show_add_modal.set(true) on:click=move |_| set_show_add_modal.set(true)
> >
@@ -29,11 +29,11 @@ pub fn Toolbar() -> impl IntoView {
</div> </div>
<div class="navbar-end gap-2 px-4"> <div class="navbar-end gap-2 px-4">
<div class="join"> <div class="join glass-input rounded-lg p-0.5">
<input <input
type="text" type="text"
placeholder="Search..." placeholder="Search..."
class="input input-sm input-bordered join-item w-full max-w-xs focus:outline-none" class="input input-sm input-ghost join-item w-full max-w-xs focus:outline-none focus:bg-transparent placeholder-gray-400 text-white"
prop:value=move || store.search_query.get() prop:value=move || store.search_query.get()
on:input=move |ev| store.search_query.set(event_target_value(&ev)) on:input=move |ev| store.search_query.set(event_target_value(&ev))
on:keydown=move |ev: web_sys::KeyboardEvent| { on:keydown=move |ev: web_sys::KeyboardEvent| {
@@ -44,11 +44,11 @@ pub fn Toolbar() -> impl IntoView {
/> />
<Show when=move || !store.search_query.get().is_empty()> <Show when=move || !store.search_query.get().is_empty()>
<button <button
class="btn btn-sm btn-ghost join-item border-base-content/20 border-l-0 px-2" class="btn btn-sm btn-ghost join-item px-2 text-white/50 hover:text-white"
title="Clear Search" title="Clear Search"
on:click=move |_| store.search_query.set(String::new()) on:click=move |_| store.search_query.set(String::new())
> >
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 opacity-70"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>

View File

@@ -255,33 +255,33 @@ pub fn TorrentTable() -> impl IntoView {
}; };
view! { view! {
<div class="overflow-x-auto h-full bg-base-100 relative"> <div class="overflow-x-auto h-full relative p-4">
<div class="hidden md:block h-full overflow-x-auto"> <div class="hidden md:block h-full overflow-x-auto glass-panel rounded-2xl">
<table class="table table-sm table-pin-rows w-full max-w-full whitespace-nowrap"> <table class="table table-sm table-pin-rows w-full max-w-full whitespace-nowrap">
<thead> <thead>
<tr class="text-xs uppercase text-base-content/60 border-b border-base-200"> <tr class="text-xs uppercase text-white/50 border-b border-white/5 bg-gray-900/40 backdrop-blur-md">
<th class="cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::Name)> <th class="cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::Name)>
<div class="flex items-center">"Name" {move || sort_arrow(SortColumn::Name)}</div> <div class="flex items-center">"Name" {move || sort_arrow(SortColumn::Name)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::Size)> <th class="w-24 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::Size)>
<div class="flex items-center">"Size" {move || sort_arrow(SortColumn::Size)}</div> <div class="flex items-center">"Size" {move || sort_arrow(SortColumn::Size)}</div>
</th> </th>
<th class="w-48 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::Progress)> <th class="w-48 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::Progress)>
<div class="flex items-center">"Progress" {move || sort_arrow(SortColumn::Progress)}</div> <div class="flex items-center">"Progress" {move || sort_arrow(SortColumn::Progress)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::Status)> <th class="w-24 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::Status)>
<div class="flex items-center">"Status" {move || sort_arrow(SortColumn::Status)}</div> <div class="flex items-center">"Status" {move || sort_arrow(SortColumn::Status)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::DownSpeed)> <th class="w-24 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::DownSpeed)>
<div class="flex items-center">"Down Speed" {move || sort_arrow(SortColumn::DownSpeed)}</div> <div class="flex items-center">"Down Speed" {move || sort_arrow(SortColumn::DownSpeed)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::UpSpeed)> <th class="w-24 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::UpSpeed)>
<div class="flex items-center">"Up Speed" {move || sort_arrow(SortColumn::UpSpeed)}</div> <div class="flex items-center">"Up Speed" {move || sort_arrow(SortColumn::UpSpeed)}</div>
</th> </th>
<th class="w-24 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::ETA)> <th class="w-24 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::ETA)>
<div class="flex items-center">"ETA" {move || sort_arrow(SortColumn::ETA)}</div> <div class="flex items-center">"ETA" {move || sort_arrow(SortColumn::ETA)}</div>
</th> </th>
<th class="w-32 cursor-pointer hover:bg-base-300 group select-none" on:click=move |_| handle_sort(SortColumn::AddedDate)> <th class="w-32 cursor-pointer hover:bg-white/5 group select-none py-4" on:click=move |_| handle_sort(SortColumn::AddedDate)>
<div class="flex items-center">"Date" {move || sort_arrow(SortColumn::AddedDate)}</div> <div class="flex items-center">"Date" {move || sort_arrow(SortColumn::AddedDate)}</div>
</th> </th>
</tr> </tr>
@@ -295,7 +295,7 @@ pub fn TorrentTable() -> impl IntoView {
shared::TorrentStatus::Downloading => "text-primary", shared::TorrentStatus::Downloading => "text-primary",
shared::TorrentStatus::Paused => "text-warning", shared::TorrentStatus::Paused => "text-warning",
shared::TorrentStatus::Error => "text-error", shared::TorrentStatus::Error => "text-error",
_ => "text-base-content/50" _ => "text-white/50"
}; };
let t_hash = t.hash.clone(); let t_hash = t.hash.clone();
let t_hash_click = t.hash.clone(); let t_hash_click = t.hash.clone();
@@ -307,7 +307,7 @@ pub fn TorrentTable() -> impl IntoView {
view! { view! {
<tr <tr
class=move || { class=move || {
let base = "hover border-b border-base-200 select-none"; let base = "hover:bg-white/5 border-b border-white/5 select-none transition-colors duration-150";
if is_selected_fn() { if is_selected_fn() {
format!("{} bg-primary/10", base) format!("{} bg-primary/10", base)
} else { } else {
@@ -323,21 +323,21 @@ pub fn TorrentTable() -> impl IntoView {
move |_| set_selected_hash.set(Some(t_hash.clone())) move |_| set_selected_hash.set(Some(t_hash.clone()))
} }
> >
<td class="font-medium truncate max-w-xs" title={t.name.clone()}> <td class="font-medium truncate max-w-xs py-3 text-white" title={t.name.clone()}>
{t.name} {t.name}
</td> </td>
<td class="opacity-80 font-mono text-[11px]">{format_bytes(t.size)}</td> <td class="opacity-80 font-mono text-[11px] py-3 text-white/70">{format_bytes(t.size)}</td>
<td> <td class="py-3">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<progress class={format!("progress w-24 {}", progress_class)} value={t.percent_complete} max="100"></progress> <progress class={format!("progress w-24 h-1.5 {}", progress_class)} value={t.percent_complete} max="100"></progress>
<span class="text-[10px] opacity-70">{format!("{:.1}%", t.percent_complete)}</span> <span class="text-[10px] opacity-70 text-white/70">{format!("{:.1}%", t.percent_complete)}</span>
</div> </div>
</td> </td>
<td class={format!("text-[11px] font-medium {}", status_class)}>{status_str}</td> <td class={format!("text-[11px] font-medium py-3 {}", status_class)}>{status_str}</td>
<td class="text-right font-mono text-[11px] opacity-80 text-success">{format_speed(t.down_rate)}</td> <td class="text-right font-mono text-[11px] opacity-80 text-success py-3">{format_speed(t.down_rate)}</td>
<td class="text-right font-mono text-[11px] opacity-80 text-primary">{format_speed(t.up_rate)}</td> <td class="text-right font-mono text-[11px] opacity-80 text-primary py-3">{format_speed(t.up_rate)}</td>
<td class="text-right font-mono text-[11px] opacity-80">{format_duration(t.eta)}</td> <td class="text-right font-mono text-[11px] opacity-80 py-3 text-white/70">{format_duration(t.eta)}</td>
<td class="text-right font-mono text-[11px] opacity-80 whitespace-nowrap">{format_date(t.added_date)}</td> <td class="text-right font-mono text-[11px] opacity-80 whitespace-nowrap py-3 text-white/70">{format_date(t.added_date)}</td>
</tr> </tr>
} }
}).collect::<Vec<_>>()} }).collect::<Vec<_>>()}
@@ -345,19 +345,19 @@ pub fn TorrentTable() -> impl IntoView {
</table> </table>
</div> </div>
<div class="md:hidden flex flex-col h-full bg-base-200 relative cursor-pointer"> <div class="md:hidden flex flex-col h-full relative cursor-pointer">
<div class="px-3 py-2 border-b border-base-200 flex justify-between items-center bg-base-100/95 backdrop-blur z-10 shrink-0 cursor-default"> <div class="px-3 py-2 flex justify-between items-center glass-header z-10 shrink-0 cursor-default rounded-b-xl mb-3">
<span class="text-xs font-bold opacity-50 uppercase tracking-wider">"Torrents"</span> <span class="text-xs font-bold text-primary uppercase tracking-wider">"Torrents"</span>
<details class="dropdown dropdown-end" node_ref=sort_details_ref> <details class="dropdown dropdown-end" node_ref=sort_details_ref>
<summary class="btn btn-ghost btn-xs gap-1 opacity-70 font-normal list-none [&::-webkit-details-marker]:hidden cursor-pointer"> <summary class="btn btn-ghost btn-xs gap-1 opacity-70 font-normal list-none [&::-webkit-details-marker]:hidden cursor-pointer text-white">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 pointer-events-none"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 pointer-events-none">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7.5L7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5" /> <path stroke-linecap="round" stroke-linejoin="round" d="M3 7.5L7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5" />
</svg> </svg>
<span class="pointer-events-none">"Sort"</span> <span class="pointer-events-none">"Sort"</span>
</summary> </summary>
<ul class="dropdown-content z-[100] menu p-2 shadow bg-base-100 rounded-box w-48 mt-1 border border-base-200 text-xs cursor-default"> <ul class="dropdown-content z-[100] menu p-2 shadow-xl glass-panel rounded-box w-48 mt-1 text-xs cursor-default">
<li class="menu-title px-2 py-1 opacity-50 text-[10px] uppercase font-bold">"Sort By"</li> <li class="menu-title px-2 py-1 opacity-50 text-[10px] uppercase font-bold text-white">"Sort By"</li>
{ {
let columns = vec![ let columns = vec![
(SortColumn::Name, "Name"), (SortColumn::Name, "Name"),
@@ -378,7 +378,7 @@ pub fn TorrentTable() -> impl IntoView {
<li> <li>
<button <button
type="button" type="button"
class=move || if is_active() { "bg-primary/10 text-primary font-bold flex justify-between" } else { "flex justify-between" } class=move || if is_active() { "bg-primary/20 text-primary font-bold flex justify-between hover:bg-primary/30" } else { "flex justify-between text-white/80 hover:bg-white/10" }
on:click=move |_| { on:click=move |_| {
handle_sort(col); handle_sort(col);
if let Some(el) = sort_details_ref.get_untracked() { if let Some(el) = sort_details_ref.get_untracked() {
@@ -408,11 +408,11 @@ pub fn TorrentTable() -> impl IntoView {
let progress_class = if t.percent_complete >= 100.0 { "progress-success" } else { "progress-primary" }; let progress_class = if t.percent_complete >= 100.0 { "progress-success" } else { "progress-primary" };
let status_str = format!("{:?}", t.status); let status_str = format!("{:?}", t.status);
let status_badge_class = match t.status { let status_badge_class = match t.status {
shared::TorrentStatus::Seeding => "badge-success badge-soft", shared::TorrentStatus::Seeding => "badge-success badge-soft bg-success/20 text-success border-0",
shared::TorrentStatus::Downloading => "badge-primary badge-soft", shared::TorrentStatus::Downloading => "badge-primary badge-soft bg-primary/20 text-primary border-0",
shared::TorrentStatus::Paused => "badge-warning badge-soft", shared::TorrentStatus::Paused => "badge-warning badge-soft bg-warning/20 text-warning border-0",
shared::TorrentStatus::Error => "badge-error badge-soft", shared::TorrentStatus::Error => "badge-error badge-soft bg-error/20 text-error border-0",
_ => "badge-ghost" _ => "badge-ghost bg-white/10 text-white/50 border-0"
}; };
let _t_hash = t.hash.clone(); let _t_hash = t.hash.clone();
let t_hash_click = t.hash.clone(); let t_hash_click = t.hash.clone();
@@ -459,7 +459,7 @@ pub fn TorrentTable() -> impl IntoView {
view! { view! {
<div <div
class=move || { class=move || {
"card card-compact bg-base-100 shadow-sm border border-base-200 transition-transform active:scale-[0.99] select-none cursor-pointer" "card card-compact glass-card transition-transform active:scale-[0.99] select-none cursor-pointer"
} }
style="user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;" style="user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;"
on:contextmenu={ on:contextmenu={
@@ -477,36 +477,36 @@ pub fn TorrentTable() -> impl IntoView {
> >
<div class="card-body gap-3"> <div class="card-body gap-3">
<div class="flex justify-between items-start gap-2"> <div class="flex justify-between items-start gap-2">
<h3 class="font-medium text-sm line-clamp-2 leading-tight">{t.name}</h3> <h3 class="font-medium text-sm line-clamp-2 leading-tight text-white">{t.name}</h3>
<div class={format!("badge badge-xs text-[10px] whitespace-nowrap {}", status_badge_class)}> <div class={format!("badge badge-xs text-[10px] whitespace-nowrap {}", status_badge_class)}>
{status_str} {status_str}
</div> </div>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<div class="flex justify-between text-[10px] opacity-70"> <div class="flex justify-between text-[10px] opacity-70 text-white/70">
<span>{format_bytes(t.size)}</span> <span>{format_bytes(t.size)}</span>
<span>{format!("{:.1}%", t.percent_complete)}</span> <span>{format!("{:.1}%", t.percent_complete)}</span>
</div> </div>
<progress class={format!("progress w-full h-1.5 {}", progress_class)} value={t.percent_complete} max="100"></progress> <progress class={format!("progress w-full h-1.5 {}", progress_class)} value={t.percent_complete} max="100"></progress>
</div> </div>
<div class="grid grid-cols-4 gap-2 text-[10px] font-mono opacity-80 pt-1 border-t border-base-200/50"> <div class="grid grid-cols-4 gap-2 text-[10px] font-mono opacity-80 pt-1 border-t border-white/10">
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-[9px] opacity-60 uppercase">"Down"</span> <span class="text-[9px] opacity-60 uppercase text-white/50">"Down"</span>
<span class="text-success">{format_speed(t.down_rate)}</span> <span class="text-success">{format_speed(t.down_rate)}</span>
</div> </div>
<div class="flex flex-col text-center border-l border-r border-base-200/50"> <div class="flex flex-col text-center border-l border-r border-white/10">
<span class="text-[9px] opacity-60 uppercase">"Up"</span> <span class="text-[9px] opacity-60 uppercase text-white/50">"Up"</span>
<span class="text-primary">{format_speed(t.up_rate)}</span> <span class="text-primary">{format_speed(t.up_rate)}</span>
</div> </div>
<div class="flex flex-col text-center border-r border-base-200/50"> <div class="flex flex-col text-center border-r border-white/10">
<span class="text-[9px] opacity-60 uppercase">"ETA"</span> <span class="text-[9px] opacity-60 uppercase text-white/50">"ETA"</span>
<span>{format_duration(t.eta)}</span> <span class="text-white/70">{format_duration(t.eta)}</span>
</div> </div>
<div class="flex flex-col text-right"> <div class="flex flex-col text-right">
<span class="text-[9px] opacity-60 uppercase">"Date"</span> <span class="text-[9px] opacity-60 uppercase text-white/50">"Date"</span>
<span>{format_date(t.added_date)}</span> <span class="text-white/70">{format_date(t.added_date)}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,13 +3,56 @@ module.exports = {
content: ["./index.html", "./src/**/*.{rs,html}"], content: ["./index.html", "./src/**/*.{rs,html}"],
theme: { theme: {
extend: { extend: {
fontFamily: {
sans: ['"Space Grotesk"', 'sans-serif'],
},
colors: { colors: {
primary: {
DEFAULT: "#f49d25",
focus: "#d68315",
content: "#ffffff",
},
gray: { gray: {
900: "#111827", 900: "#111827",
800: "#1f2937", 800: "#1f2937",
700: "#374151", 700: "#374151",
glass: "rgba(31, 41, 55, 0.7)",
}, },
glass: {
100: "rgba(255, 255, 255, 0.1)",
200: "rgba(255, 255, 255, 0.2)",
border: "rgba(255, 255, 255, 0.1)",
}
}, },
backgroundImage: {
'glass-gradient': 'linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.01) 100%)',
}
}, },
}, },
}; plugins: [require("daisyui")],
daisyui: {
themes: [
{
vibeglass: {
"primary": "#f49d25",
"secondary": "#3b82f6",
"accent": "#10b981",
"neutral": "#1f2937",
"base-100": "#0f172a", // Çok koyu mavi/siyah arka plan
"base-200": "#1e293b",
"base-300": "#334155",
"base-content": "#f3f4f6",
"info": "#3abff8",
"success": "#36d399",
"warning": "#fbbd23",
"error": "#f87272",
"--rounded-box": "0.5rem", // ROUND_EIGHT
"--rounded-btn": "0.5rem",
"--glass-blur": "12px",
"--glass-opacity": "0.7",
},
},
"dark",
],
},
};