use crate::{
components::{
flag_panel::FlagPanel, mode_selector::ModeSelector, output_log::OutputLog,
preset_panel::PresetPanel, terminal_panel::TerminalPanel, url_bar::UrlBar,
},
core::{
download_mode::{DownloadSource, DownloadType, Quality},
flags::Flag,
presets::{default_preset, resolve_preset_flags, Preset},
runner::{self, ChildHandle},
},
};
use dioxus::prelude::*;
use std::sync::{Arc, Mutex};
#[component]
pub fn App() -> Element {
let download_type: Signal<DownloadType> = use_signal(DownloadType::default);
let download_source: Signal<DownloadSource> = use_signal(DownloadSource::default);
let quality: Signal<Quality> = use_signal(Quality::default);
let url = use_signal::<String>(String::new);
let batch_file = use_signal::<String>(String::new);
let archive_file = use_signal::<String>(String::new);
let output_dir = use_signal(|| {
dirs::download_dir()
.unwrap_or_else(|| std::path::PathBuf::from("."))
.to_string_lossy()
.to_string()
});
let initial_preset = default_preset();
let initial_flags = resolve_preset_flags(&initial_preset);
let active_flags: Signal<Vec<Flag>> = use_signal(|| initial_flags);
let active_preset: Signal<Option<Preset>> = use_signal(|| Some(initial_preset));
let log_lines: Signal<Vec<String>> = use_signal(Vec::new);
let is_running = use_signal(|| false);
let child_handle: Signal<ChildHandle> = use_signal(|| Arc::new(Mutex::new(None)));
let built_command = use_memo(move || {
runner::build_command_string(&runner::DownloadRequest {
url: url.read().clone(),
batch_file: batch_file.read().clone(),
archive_file: archive_file.read().clone(),
download_type: download_type.read().clone(),
download_source: download_source.read().clone(),
quality: quality.read().clone(),
output_dir: output_dir.read().clone(),
extra_flags: active_flags.read().clone(),
})
});
rsx! {
div {
style: "
display: flex;
flex-direction: column;
height: 100vh;
background: #0b0b14;
color: #e0e0e0;
font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
overflow: hidden;
",
div {
style: "
padding: 12px 20px;
background: #0f0f1e;
border-bottom: 2px solid #6c63ff;
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
",
span { style: "font-size: 20px;", "📥" }
h1 {
style: "
margin: 0;
font-size: 16px;
color: #6c63ff;
letter-spacing: 2px;
font-weight: bold;
",
"nomnom"
}
span {
style: "
font-size: 11px;
color: #444;
letter-spacing: 1px;
margin-left: 4px;
",
"gib me URLs"
}
div { style: "flex: 1;" }
if *is_running.read() {
div {
style: "
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: #6c63ff;
",
span { "⏳" }
"Downloading…"
}
}
}
div {
style: "
display: flex;
flex: 1;
overflow: hidden;
",
div {
style: "
width: 280px;
min-width: 240px;
background: #0e0e1c;
border-right: 1px solid #1e1e36;
display: flex;
flex-direction: column;
overflow-y: auto;
padding: 14px 10px;
gap: 16px;
",
PresetPanel {
active_preset,
active_flags,
download_type,
download_source,
quality,
}
div {
style: "
height: 1px;
background: #1e1e36;
margin: 0 4px;
"
}
FlagPanel { active_flags }
}
div {
style: "
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 12px;
gap: 10px;
",
ModeSelector {
download_type,
download_source,
quality,
active_preset,
}
UrlBar {
download_type,
download_source,
quality,
url,
batch_file,
archive_file,
output_dir,
built_command,
active_flags,
log_lines,
is_running,
child_handle,
}
TerminalPanel {
log_lines,
is_running,
child_handle,
}
OutputLog { log_lines }
}
}
}
}
}