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

@@ -14,9 +14,10 @@ import {
import { CommandPalette } from "./CommandPalette";
import { CardEntry } from "./views/CardEntry";
import { LibraryBrowser } from "./views/LibraryBrowser";
import { CardSearch } from "./views/CardSearch";
import { DeckView } from "./views/DeckView";
type Tab = "add" | "library" | "decks";
type Tab = "add" | "library" | "search" | "decks";
const svg = (children: ReactNode, size = 20) => (
<svg
@@ -94,6 +95,16 @@ const NAV: { id: Tab; label: string; icon: ReactNode }[] = [
</>,
),
},
{
id: "search",
label: "Card Search",
icon: svg(
<>
<circle cx="11" cy="11" r="7" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</>,
),
},
{
id: "decks",
label: "Decks",
@@ -439,6 +450,8 @@ export default function App() {
<CardEntry />
) : tab === "library" ? (
<LibraryBrowser onOfferUndo={offerUndo} />
) : tab === "search" ? (
<CardSearch onOfferUndo={offerUndo} />
) : (
<DeckView refreshSignal={deckRefreshSignal} />
)}