Big UI Overhaul
This commit is contained in:
120
src/main.rs
120
src/main.rs
@@ -190,21 +190,21 @@ impl Default for App {
|
||||
let (view_stack, config_status) = match &loaded {
|
||||
Some(cfg) if cfg.access_token.is_some() => (
|
||||
vec![View::Categories],
|
||||
format!("auth: token present ({})", cfg.base_url),
|
||||
format!("Connected: ({})", cfg.base_url),
|
||||
),
|
||||
Some(cfg) => (
|
||||
vec![View::Login],
|
||||
format!("auth: needs login ({})", cfg.base_url),
|
||||
format!("Not Connected: ({})", cfg.base_url),
|
||||
),
|
||||
None => (
|
||||
vec![View::Login],
|
||||
"config: missing/invalid (need base_url)".to_string(),
|
||||
"Missing Configuration".to_string(),
|
||||
),
|
||||
};
|
||||
|
||||
Self {
|
||||
should_exit: false,
|
||||
status: "Press q to quit".to_string(),
|
||||
status: String::new(),
|
||||
config_status,
|
||||
config: loaded,
|
||||
|
||||
@@ -486,7 +486,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
if !matches!(app.current_view(), View::Categories) {
|
||||
app.view_stack = vec![View::Categories];
|
||||
app.selected = 0;
|
||||
app.status = "Home".into();
|
||||
app.status = "$".into();
|
||||
return;
|
||||
} else {
|
||||
app.should_exit = true;
|
||||
@@ -702,7 +702,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
page_size: PAGE_SIZE,
|
||||
loading: false,
|
||||
});
|
||||
app.status = "Loaded".into();
|
||||
app.status = "$".into();
|
||||
}
|
||||
Err(e) => app.status = format!("Fetch failed: {e}"),
|
||||
}
|
||||
@@ -782,7 +782,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
page_size: PAGE_SIZE,
|
||||
loading: false,
|
||||
});
|
||||
app.status = "Loaded".into();
|
||||
app.status = "$".into();
|
||||
}
|
||||
Err(e) => app.status = format!("Playlist load failed: {e}"),
|
||||
}
|
||||
@@ -791,7 +791,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
// Playables -> mpv
|
||||
// Playables: do nothing on Enter (use 'p' to play)
|
||||
if ty == "Episode" || ty == "Audio" || ty == "Movie" {
|
||||
app.status = "Press 'p' to play".into();
|
||||
app.status = "Press 'p' to play, or 'P' for autoplay.".into();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -806,7 +806,7 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
KeyCode::Esc | KeyCode::Backspace => {
|
||||
if app.view_stack.len() > 1 {
|
||||
app.view_stack.pop();
|
||||
app.status = "Went back".to_string();
|
||||
app.status = "$".to_string();
|
||||
app.selected = 0;
|
||||
}
|
||||
}
|
||||
@@ -816,7 +816,8 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
if app.items.is_empty() { return; }
|
||||
if app.selected == 0 { app.selected = app.items.len() - 1; }
|
||||
else { app.selected -= 1; }
|
||||
app.status = format!("Selected: {}", app.items[app.selected]);
|
||||
//app.status = format!("Selected: {}", app.items[app.selected]);
|
||||
app.status = "$".into();
|
||||
}
|
||||
View::Library { items, selected, total, .. } => {
|
||||
if items.is_empty() { return; }
|
||||
@@ -846,7 +847,8 @@ fn handle_key(app: &mut App, code: KeyCode) {
|
||||
View::Categories => {
|
||||
if app.items.is_empty() { return; }
|
||||
app.selected = (app.selected + 1) % app.items.len();
|
||||
app.status = format!("Selected: {}", app.items[app.selected]);
|
||||
//app.status = format!("Selected: {}", app.items[app.selected]);
|
||||
app.status = "$".into();
|
||||
}
|
||||
View::Library { items, selected, total, .. } => {
|
||||
if items.is_empty() { return; }
|
||||
@@ -1125,9 +1127,29 @@ fn ui(frame: &mut Frame, app: &App) {
|
||||
let panes = layout(area);
|
||||
|
||||
// Top
|
||||
//frame.render_widget(
|
||||
// Paragraph::new("cj-jftui").block(Block::bordered()),
|
||||
// panes.top,
|
||||
//);
|
||||
|
||||
let top_block = Block::bordered();
|
||||
frame.render_widget(top_block.clone(), panes.top);
|
||||
|
||||
let top_inner = top_block.inner(panes.top);
|
||||
let [top_l, top_r] = Layout::horizontal([Constraint::Fill(1), Constraint::Length(40)])
|
||||
.areas(top_inner);
|
||||
|
||||
frame.render_widget(Paragraph::new("cj-jftui"), top_l);
|
||||
let server = app
|
||||
.config
|
||||
.as_ref()
|
||||
.map(|c| c.base_url.clone())
|
||||
.unwrap_or_else(|| "-".into());
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new("cj-jftui").block(Block::bordered()),
|
||||
panes.top,
|
||||
Paragraph::new(server)
|
||||
.alignment(ratatui::layout::Alignment::Right),
|
||||
top_r,
|
||||
);
|
||||
|
||||
// Main
|
||||
@@ -1163,44 +1185,60 @@ fn ui(frame: &mut Frame, app: &App) {
|
||||
}
|
||||
|
||||
// Bottom Left
|
||||
let status_text = if let Some(s) = &app.search {
|
||||
// ---- Bottom Left: prompt / mpv ----
|
||||
let left_text = if let Some(s) = &app.search {
|
||||
// search takes over temporarily
|
||||
let scope = match &s.scope {
|
||||
SearchScope::All => "All".to_string(),
|
||||
SearchScope::Library { title, .. } => title.clone(),
|
||||
};
|
||||
|
||||
let mode = s.mode.label();
|
||||
|
||||
if s.input.is_empty() {
|
||||
format!("/ ({scope}) [{mode}]")
|
||||
} else {
|
||||
format!("/ ({scope}) [{mode}] {}", s.input)
|
||||
}
|
||||
} else if app.mpv.is_some() {
|
||||
// mpv persists while browsing
|
||||
if app.debug_mpv {
|
||||
fmt_mpv_state(&app.mpv_state)
|
||||
} else if let Some(p) = app.mpv_state.percent {
|
||||
if p.is_finite() { format!("mpv: {p:5.1}%") } else { "mpv: playing".into() }
|
||||
} else {
|
||||
"mpv: playing".into()
|
||||
}
|
||||
} else {
|
||||
app.status.clone()
|
||||
"$".into()
|
||||
};
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new(status_text)
|
||||
.block(Block::bordered().title("Status").merge_borders(MergeStrategy::Exact)),
|
||||
Paragraph::new(left_text)
|
||||
// no title
|
||||
.block(Block::bordered().merge_borders(MergeStrategy::Exact)),
|
||||
panes.left_output,
|
||||
);
|
||||
|
||||
// Bottom Right
|
||||
let info = match app.current_view() {
|
||||
View::Library { items, total, loading, .. } => {
|
||||
if *loading {
|
||||
format!("loading… {}/{}", items.len(), total)
|
||||
} else {
|
||||
format!("{}/{}", items.len(), total)
|
||||
}
|
||||
}
|
||||
_ => app.config_status.clone(),
|
||||
|
||||
// ---- Bottom Right: always-on status/help ----
|
||||
let connected = if app.config.as_ref().and_then(|c| c.access_token.as_ref()).is_some() {
|
||||
"Connected ✅"
|
||||
} else {
|
||||
"Disconnected ❌"
|
||||
};
|
||||
|
||||
let server = app
|
||||
.config
|
||||
.as_ref()
|
||||
.map(|c| c.base_url.as_str())
|
||||
.unwrap_or("-");
|
||||
|
||||
let right_text = format!(
|
||||
"{connected}\n\np/P: play/autoplay\nd: download\nq: quit\n/help: more help"
|
||||
);
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new(info)
|
||||
.block(Block::bordered().title("Info").merge_borders(MergeStrategy::Exact)),
|
||||
Paragraph::new(right_text)
|
||||
.wrap(Wrap { trim: false })
|
||||
.block(Block::bordered().merge_borders(MergeStrategy::Exact)),
|
||||
panes.right_output,
|
||||
);
|
||||
}
|
||||
@@ -1247,15 +1285,9 @@ fn render_categories(frame: &mut Frame, app: &App, left: Rect, right: Rect) {
|
||||
frame.render_stateful_widget(list, left, &mut state);
|
||||
|
||||
// Right pane details (use the real selected item)
|
||||
let detail = if len > 0 {
|
||||
format!("Details for:\n\n{}", app.items[selected])
|
||||
} else {
|
||||
"No items".to_string()
|
||||
};
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new(detail)
|
||||
.block(Block::bordered().title("Details").merge_borders(MergeStrategy::Exact)),
|
||||
Paragraph::new("")
|
||||
.block(Block::bordered().merge_borders(MergeStrategy::Exact)),
|
||||
right,
|
||||
);
|
||||
}
|
||||
@@ -1399,7 +1431,7 @@ fn open_children(app: &mut App, title: String, query: core::jellyfin::LibraryQue
|
||||
page_size: PAGE_SIZE,
|
||||
loading: false,
|
||||
});
|
||||
app.status = "Loaded".into();
|
||||
app.status = "$".into();
|
||||
}
|
||||
Err(e) => app.status = format!("Fetch failed: {e}"),
|
||||
}
|
||||
@@ -1694,7 +1726,7 @@ fn autoplay_tick(app: &mut App) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
app.autoplay = false;
|
||||
app.status = "autoplay finished".into();
|
||||
app.status = "$".into();
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -1704,7 +1736,7 @@ fn autoplay_tick(app: &mut App) {
|
||||
_ => {
|
||||
app.autoplay = false;
|
||||
app.autoplay_next_index = None;
|
||||
app.status = "autoplay stopped".into();
|
||||
app.status = "$".into();
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -1741,7 +1773,7 @@ fn autoplay_tick(app: &mut App) {
|
||||
app.autoplay_prev_time_pos = None;
|
||||
|
||||
if !app.debug_mpv {
|
||||
app.status = "autoplay next".into();
|
||||
app.status = "$".into();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user