Buy cards: store links, bulk buying from decks, and custom stores

The card preview gets Buy buttons that search TCGplayer, Card Kingdom and Star City Games by name. The deck bulk bar gets a Buy… action that sends the copies the selected cards are missing to TCGplayer or Card Kingdom as one pre-filled list.

The Info tab gets a Stores panel for adding your own shops, kept per device. An address is enough for TCGplayer Pro and Shopify storefronts, and any other shop can use a search link with {card} in it. Custom stores join the preview's Buy buttons and the bulk Buy… dialog, which opens a search per card because none of the shops tried can take a list from a link. On Android those open one tap at a time: once the browser is in front, Android blocks further links from the app as background activity starts.

On wide screens the Info tab's library stats move to the top of the left column.

Closes #32
This commit is contained in:
2026-11-01 10:25:14 -06:00
parent 27bc5c9844
commit c7572ae7b0
6 changed files with 437 additions and 7 deletions

View File

@@ -814,7 +814,8 @@ input[type="checkbox"] {
/* Info page: a column of panels on narrow screens, two columns on wide ones.
Below the breakpoint the two `.info-col` wrappers dissolve (display:
contents) so `order` can interleave their panels into the reading order
the single column always had: card data, libraries, sync, summary, about. */
the single column always had: card data, libraries, stores, sync, summary,
about. */
.info-grid {
display: flex;
flex-direction: column;
@@ -831,9 +832,10 @@ input[type="checkbox"] {
}
.info-db { order: 1; }
.info-libraries { order: 2; }
.info-sync { order: 3; }
.info-summary { order: 4; }
.info-about { order: 5; }
.info-stores { order: 3; }
.info-sync { order: 4; }
.info-summary { order: 5; }
.info-about { order: 6; }
@media (min-width: 900px) {
.info-grid {
display: grid;
@@ -847,6 +849,7 @@ input[type="checkbox"] {
gap: var(--space-4);
min-width: 0;
}
.info-summary { order: 0; }
}
.info-about-row {
display: flex;
@@ -1349,6 +1352,26 @@ button.legality-tile-select {
justify-content: flex-end;
gap: 8px;
}
.buy-dialog {
display: flex;
flex-direction: column;
gap: 12px;
}
.buy-summary {
display: flex;
flex-direction: column;
gap: 2px;
}
/* A shared grid, so a lone store lines up with the column above it rather than
stretching across the dialog. */
.buy-stores {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
gap: 8px;
}
.buy-stores button {
justify-content: center;
}
/* ============================================================
Lists (search results, deck list)