Initial commit: Card Grader deployed to hippofam.com/cards

PWA card-grading app, deployed behind Nginx Proxy Manager on Unraid with
basic auth. Includes CARD_GRADER_BASE_PATH support for running under a
sub-path, and Docker/compose config for the Unraid deployment.
This commit is contained in:
Barely Removable 2026-08-22 09:22:41 -07:00
commit c7bd71a3e1
19 changed files with 3618 additions and 0 deletions

90
static/index.html Normal file
View file

@ -0,0 +1,90 @@
<!doctype html>
<html lang="en" data-base="__BASE__">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Card Grader</title>
<link rel="manifest" href="__BASE__/static/manifest.json">
<link rel="icon" href="__BASE__/static/icon.svg">
<meta name="theme-color" content="#16224a">
<!-- iOS ignores the web app manifest for install behaviour and needs its own
meta tags. Without these, "Add to Home Screen" on an iPhone produces a
bookmark that opens in Safari with the address bar showing, rather than
something that looks and behaves like an installed app. -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Card Grader">
<link rel="apple-touch-icon" href="__BASE__/static/icon-180.png">
<link rel="stylesheet" href="__BASE__/static/style.css">
</head>
<body>
<header class="topbar">
<div class="brand">
<span class="brand-mark"></span>
<h1>Card Grader</h1>
</div>
<div class="topbar-actions">
<button id="btn-install" class="btn" hidden>Install app</button>
<button id="btn-settings" class="btn btn-quiet">Settings</button>
</div>
</header>
<div id="banner" class="banner" hidden></div>
<main>
<!-- ------------------------------------------------------------- grade -->
<section class="panel">
<div class="panel-head">
<h2>Grade a card</h2>
<span class="hint" id="model-cost-hint"></span>
</div>
<div class="note">Front straight-on is the minimum. Adding the back lets it judge
back centering, and it can only tell a print line from a crease — about five PSA
grades apart — if it can check both sides.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:12px">
<button id="btn-grade" class="btn">Choose photo(s)…</button>
<input id="grade-label" class="input" placeholder="Card name (optional, for your history)" style="flex:1 1 220px">
</div>
<input id="grade-file" type="file" accept="image/*" multiple hidden>
<div id="grade-status" class="search-status" hidden></div>
<div id="grade-review"></div>
</section>
<!-- ----------------------------------------------------------- history -->
<section class="panel">
<div class="panel-head">
<h2>History</h2>
<span class="hint" id="history-count"></span>
</div>
<div class="table-scroll">
<table class="grid" id="history-table">
<thead>
<tr>
<th class="col-card">Card</th>
<th>Grade</th>
<th>Confidence</th>
<th>When</th>
<th></th>
</tr>
</thead>
<tbody id="history-body"></tbody>
</table>
</div>
<p id="history-empty" class="empty" hidden>Nothing graded yet.</p>
</section>
</main>
<!-- ----------------------------------------------------------- settings -->
<div id="modal-scrim" class="scrim" hidden></div>
<div id="settings-modal" class="modal" hidden role="dialog" aria-label="Settings"></div>
<!-- ------------------------------------------------------- history entry -->
<div id="entry-scrim" class="scrim" hidden></div>
<div id="entry-modal" class="modal" hidden role="dialog" aria-label="Grade detail"></div>
<script src="__BASE__/static/app.js"></script>
</body>
</html>