Add a Card Search tab and fix horizontal-card (Battle/Plane) preview orientation

Card Search browses the whole downloaded card universe (not just owned
cards), reusing the Library tab's filter/group/sort/list/tiles UI; results
show ownership and offer quick "own" and "add to deck" actions.

Battles, Planes, and Phenomena store their Scryfall image portrait-shaped
with the content itself rotated, so hover/click previews now detect this
(a new `horizontal` flag threaded through ingest and every card query, with
graceful fallback for a reference.db built before the column existed) and
rotate the image back into place. Single-faced and non-DFC cases are
verified fixed; a double-faced Battle's front face still needs another pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 20:55:17 -05:00
parent 3c625a9830
commit 2b361c4a85
19 changed files with 1076 additions and 102 deletions

View File

@@ -75,6 +75,19 @@ pub fn attach_reference(conn: &Connection, reference_path: &Path) -> Result<()>
Ok(())
}
/// Whether `column` exists on `table` in the attached reference db.
/// reference.db is rebuilt (not migrated) on every ingest, so a db built
/// before a given column shipped simply lacks it — this lets callers degrade
/// gracefully (or, for narrowly-used columns, fail with a clear "update card
/// data" message) instead of a raw "no such column" SQL error.
pub fn has_reference_column(conn: &Connection, table: &str, column: &str) -> Result<bool> {
Ok(conn.query_row(
&format!("SELECT COUNT(*) FROM pragma_table_info('{table}', '{REF_DB}') WHERE name = ?1"),
[column],
|r| r.get::<_, i64>(0),
)? > 0)
}
/// Apply any library migrations newer than the recorded `user_version`.
pub fn migrate_library(conn: &Connection) -> Result<()> {
let current: i64 = conn.pragma_query_value(None, "user_version", |r| r.get(0))?;