diff --git a/cmd_chat/cardex.py b/cmd_chat/cardex.py index d8b5ae6..8458ccb 100644 --- a/cmd_chat/cardex.py +++ b/cmd_chat/cardex.py @@ -221,6 +221,7 @@ class Card: stats: dict = field(default_factory=dict) # HP/Attack/Defense/SpAtk/Speed/SpDef subscores: dict = field(default_factory=dict) shiny: bool = False # deterministic "holo pull" + status: str = "" # VM build status (done / in_progress / …) flavor: str = "" def to_dict(self) -> dict: @@ -280,8 +281,10 @@ def card_from_entry(entry: dict, manifest: dict | None = None) -> Card: flavor = (f"A {rarity.lower()} {ty}-type born from {purpose.rstrip('.')}. " f"Base stat total {sum(stats.values())}.") + status = (entry.get("status") or "unknown").strip().lower() return Card(label=label, name=name, dex=_dex(label), rarity=rarity, power=power, - types=types, stats=stats, subscores=subs, shiny=shiny, flavor=flavor) + types=types, stats=stats, subscores=subs, shiny=shiny, + status=status, flavor=flavor) # ── registry I/O + CLI ──────────────────────────────────────────────────────── diff --git a/cmd_chat/cardimg.py b/cmd_chat/cardimg.py index 33a8be3..cdd7976 100644 --- a/cmd_chat/cardimg.py +++ b/cmd_chat/cardimg.py @@ -281,6 +281,10 @@ def render_card_svg(card: Card, art_data_uri: str | None = None) -> str: f'No.{card.dex:03d} · {_esc(card.label)}{holo}') p.append(f'PWR {card.power}') + # status pill on the rarity ribbon's left (done = green check, else amber) + st_done = card.status in ("done", "complete", "completed") + st_col, st_txt = (("#3fae5a", f"✓ {card.status}") if st_done + else ("#d8a23a", f"● {card.status or 'unknown'}")) # type badges (top-right under PWR) bx = W - PAD - 6 for ty in reversed(card.types): @@ -307,6 +311,12 @@ def render_card_svg(card: Card, art_data_uri: str | None = None) -> str: p.append(f'' f'{card.rarity.upper()}') + # status pill (top-left corner of the art window) + sw = 22 + len(st_txt) * 7 + p.append(f'') + p.append(f'{_esc(st_txt)}') # stat bars y0 = ART_Y + ART_H + 26 bw = W - 2 * (PAD + 8) @@ -368,6 +378,51 @@ def render_card(card: Card, out_dir: Path, backend: str, fmt: str) -> Path: return svg_path +# ── browser gallery ─────────────────────────────────────────────────────────── +RARITY_ORDER = {"Legendary": 0, "Epic": 1, "Rare": 2, "Uncommon": 3, "Common": 4} + + +def build_gallery(cards: list[Card], out_dir: Path, backend: str) -> Path: + """Render every card as an inline SVG and lay them out in one index.html.""" + out_dir.mkdir(parents=True, exist_ok=True) + cards = sorted(cards, key=lambda c: (RARITY_ORDER.get(c.rarity, 9), -c.power)) + tally: dict[str, int] = {} + cells = [] + for c in cards: + art_uri = None + if backend != "sigil": + try: + art_uri = fetch_art_data_uri(c, backend) + except Exception as e: + print(f" ! {c.label}: {backend} art failed ({e}); sigil", file=sys.stderr) + tally[c.rarity] = tally.get(c.rarity, 0) + 1 + cells.append(f'
{render_card_svg(c, art_uri)}
') + print(f" rendered {c.rarity:<10} {c.label}") + spread = " · ".join(f"{n}× {r}" for r, n in + sorted(tally.items(), key=lambda kv: RARITY_ORDER.get(kv[0], 9))) + html = ( + '' + '' + f'

hack-house VM Cardex

' + f'

{len(cards)} saved VMs · {spread}

' + f'
{"".join(cells)}
' + '') + idx = out_dir / "index.html" + idx.write_text(html) + return idx + + # ── CLI ─────────────────────────────────────────────────────────────────────── def main(argv=None) -> int: p = argparse.ArgumentParser(prog="hh-cardimg", @@ -381,6 +436,8 @@ def main(argv=None) -> int: p.add_argument("--out", default="cards", help="output directory") p.add_argument("--prompt", action="store_true", help="just print the AI-art prompt(s) and exit") + p.add_argument("--gallery", action="store_true", + help="emit a single browser index.html grid of all cards") args = p.parse_args(argv) entries = load_entries(Path(args.registry)) @@ -400,6 +457,10 @@ def main(argv=None) -> int: return 0 out = Path(args.out) + if args.gallery: + idx = build_gallery(cards, out, args.art) + print(f"gallery → {idx}") + return 0 for c in cards: path = render_card(c, out, args.art, args.format) print(f"{c.rarity:<10} {c.label:<22} → {path}")