Deck actions UI, consistent icon buttons, native select fix, sidebar stats

- DeckView: rename/duplicate/delete deck via row-hover icon actions, with
  a ConfirmDialog for delete and PromptDialog (with friendly name-
  collision errors) for rename/duplicate.
- Add a shared IconButton (plus overlay/danger variants) and replace
  every ad hoc close/remove button in the app with it: deck actions,
  remove-card-from-deck (list and tile), clear-commander, the card-
  preview close, and the ingest-progress dismiss.
- Fix native <select> (Group by, Format, In deck): only color/border were
  overridden, leaving native OS chrome (height, arrow, popup) in place.
  Drop appearance and draw a custom chevron so they match text inputs.
- Click-to-preview: show both faces of a double-faced card side by side
  on wide viewports instead of requiring Flip, and size the preview
  images larger overall.
- Sidebar footer: replace the plain "N cards" line with a "Your library"
  panel (unique/copies/decks tiles + a WUBRG color-distribution bar),
  polling get_library_stats while card data is loaded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 21:21:47 -05:00
parent 3e41c9d055
commit e57c3dec51
7 changed files with 760 additions and 94 deletions

View File

@@ -5,7 +5,7 @@
use std::path::PathBuf;
use std::sync::Mutex;
use mtgc_core::{CardRef, Deck, DeckEntry, LibraryCard, LibraryFilter};
use mtgc_core::{CardRef, Deck, DeckEntry, LibraryCard, LibraryFilter, LibraryStats};
use serde::Serialize;
use tauri::{AppHandle, Emitter, State};
@@ -127,6 +127,12 @@ pub fn search_library(db: Db, filter: LibraryFilter) -> Res<Vec<LibraryCard>> {
s.catalog()?.search_library(&filter).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn get_library_stats(db: Db) -> Res<LibraryStats> {
let s = db.lock().unwrap();
s.catalog()?.library_stats().map_err(|e| e.to_string())
}
#[tauri::command]
pub fn list_decks(db: Db) -> Res<Vec<Deck>> {
let s = db.lock().unwrap();
@@ -147,6 +153,22 @@ pub fn delete_deck(db: Db, deck_id: i64) -> Res<()> {
s.catalog()?.delete_deck(deck_id).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn rename_deck(db: Db, deck_id: i64, name: String) -> Res<()> {
let s = db.lock().unwrap();
s.catalog()?
.rename_deck(deck_id, &name)
.map_err(|e| e.to_string())
}
#[tauri::command]
pub fn duplicate_deck(db: Db, deck_id: i64, new_name: String) -> Res<Deck> {
let s = db.lock().unwrap();
s.catalog()?
.duplicate_deck(deck_id, &new_name)
.map_err(|e| e.to_string())
}
#[tauri::command]
pub fn set_deck_format(db: Db, deck_id: i64, format: String) -> Res<()> {
let s = db.lock().unwrap();

View File

@@ -30,9 +30,12 @@ pub fn run() {
commands::set_owned,
commands::set_location,
commands::search_library,
commands::get_library_stats,
commands::list_decks,
commands::create_deck,
commands::delete_deck,
commands::rename_deck,
commands::duplicate_deck,
commands::set_deck_format,
commands::set_deck_commander,
commands::set_deck_card,