diff --git a/tools/sprite-generation/gui/src/pages/SpritePage.tsx b/tools/sprite-generation/gui/src/pages/SpritePage.tsx
index 1a255a02..3a3ba710 100644
--- a/tools/sprite-generation/gui/src/pages/SpritePage.tsx
+++ b/tools/sprite-generation/gui/src/pages/SpritePage.tsx
@@ -9,11 +9,34 @@ import {
regenerateSprite,
updatePrompt,
} from '../api'
-import type { Sprite, Variant } from '../types'
+import type { RecentVariant, Sprite, Variant } from '../types'
import { colors, statusColors } from './theme'
-import { DimensionSection, ImageModal, VariantCard, variantSrc } from './SpriteComponents'
+import { DimensionSection, ImageModal, variantSrc } from './SpriteComponents'
+import { Card } from './TheaterCard'
import { Tooltip } from '../components/Tooltip'
+function variantToRecent(v: Variant, sprite: Sprite): RecentVariant {
+ return {
+ variant_id: v.id,
+ sprite_id: v.sprite_id,
+ category: sprite.category,
+ entity_id: sprite.entity_id,
+ raw_path: v.raw_path ?? '',
+ processed_path: v.processed_path,
+ seed: v.seed,
+ created_at: v.created_at,
+ rating: v.rating,
+ notes: v.notes,
+ is_approved: v.is_approved,
+ scored_by: null,
+ review_tier: null,
+ quality_json: null,
+ gates_json: null,
+ reject_reason: v.reject_reason,
+ quality_scorer: null,
+ }
+}
+
export function SpritePage(): ReactNode {
const params = useParams()
const navigate = useNavigate()
@@ -342,29 +365,28 @@ export function SpritePage(): ReactNode {
Variants
- Keys 1-{Math.min(8, variants.length)} to select, Enter to approve selected, Esc to deselect
+ Keys 1-{Math.min(8, variants.length)} to select · Enter to approve selected · Esc to deselect
{variants.map((v, i) => (
- {
- setSelectedIndex(i)
- const src = variantSrc(v)
- if (src) {
- setModalSrc(src)
- }
- }}
- onApprove={(): void => {
- void handleApprove(v.id)
- }}
- onReject={(): void => {
- void handleRejectVariant(v.id)
- }}
- />
+ style={{ width: 220, flexShrink: 0, outline: selectedIndex === i ? `2px solid ${colors.highlight}` : 'none', borderRadius: 8 }}
+ >
+ {
+ setSelectedIndex(i)
+ const src = variantSrc(v)
+ if (src) setModalSrc(src)
+ }}
+ onApprove={(): void => { load() }}
+ onReject={(): void => { void handleRejectVariant(v.id) }}
+ />
+
))}
diff --git a/tools/sprite-generation/gui/src/pages/TheaterCard.tsx b/tools/sprite-generation/gui/src/pages/TheaterCard.tsx
index ac58fdb7..2572e70d 100644
--- a/tools/sprite-generation/gui/src/pages/TheaterCard.tsx
+++ b/tools/sprite-generation/gui/src/pages/TheaterCard.tsx
@@ -232,9 +232,11 @@ export interface CardProps {
onApprove: (variantId: number) => void
onReject: (variantId: number) => void
onSelect?: (v: RecentVariant) => void
+ /** Show Approve/Reject buttons even when no scoring notes exist */
+ alwaysShowActions?: boolean
}
-export const Card = memo(forwardRef(function Card({ v, isNew, index, onApprove, onReject, onSelect }, ref) {
+export const Card = memo(forwardRef(function Card({ v, isNew, index, onApprove, onReject, onSelect, alwaysShowActions }, ref) {
const conf = useMemo(() => confidence(v), [v.notes])
const scores = useMemo(() => parseScores(v.notes), [v.notes])
const gateOk = useMemo(() => gatePassed(v), [v.notes])
@@ -360,15 +362,48 @@ export const Card = memo(forwardRef(function Card({ v
{/* ── Art frame ───────────────────────────────────────────── */}
onSelect?.(v)}
- style={{ display: 'block', cursor: 'pointer' }}
+ style={{ display: 'block', cursor: 'pointer', position: 'relative' }}
title={`#${v.variant_id} — ${v.sprite_id} (click for details)`}
>
-
+ {imgUrl ? (
+
+ ) : (
+
+ ▣
+ no image
+
+ )}
+ {v.rating === -1 && (
+
+
+ Rejected
+
+
+ )}
{/* ── Text box ────────────────────────────────────────────── */}
@@ -458,7 +493,28 @@ export const Card = memo(forwardRef(function Card({ v
}}>
APPROVED
- ) : scores && (
+ ) : v.rating === -1 ? (
+
+
+ {
+ e.stopPropagation()
+ approveVariant(v.sprite_id, v.variant_id).then(
+ () => { onApprove(v.variant_id) },
+ () => {},
+ )
+ }}
+ style={{
+ flex: 1, padding: '5px 0', borderRadius: 4, fontSize: 11, fontWeight: 600,
+ background: '#10b98133', color: '#10b981', border: '1px solid #10b98155',
+ cursor: 'pointer',
+ }}
+ >
+ Approve anyway
+
+
+
+ ) : (scores || alwaysShowActions) && imgUrl ? (
(function Card({ v
- )}
+ ) : null}
)
}))