feat(sprite-generation): Add API endpoints, UI components, and TypeScript types for sprite generation workflow

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-29 10:07:32 -07:00
parent 65f17eb684
commit f6ef679c1a
3 changed files with 39 additions and 3 deletions

View file

@ -5,6 +5,7 @@ import { ReviewQueuePage } from './pages/ReviewQueuePage'
import { CategoryPage } from './pages/CategoryPage'
import { SpritePage } from './pages/SpritePage'
import SpriteTheaterPage from './pages/SpriteTheaterPage'
import TerrainGridPage from './pages/TerrainGridPage'
import VariantPage from './pages/VariantPage'
import { SpriteStream } from './SpriteStream'
import { colors } from './pages/theme'
@ -51,6 +52,7 @@ export function App(): ReactElement {
{[
{ to: '/theater', label: 'Theater' },
{ to: '/review', label: 'Review' },
{ to: '/terrain-grid', label: 'Terrain Grid' },
].map(({ to, label }) => {
const active = location.pathname === to
return (
@ -79,6 +81,7 @@ export function App(): ReactElement {
<Route path="/review" element={<ReviewQueuePage />} />
<Route path="/category/:name" element={<CategoryPage />} />
<Route path="/variant/:id" element={<VariantPage />} />
<Route path="/terrain-grid" element={<TerrainGridPage />} />
<Route path="/sprite/*" element={<SpritePage />} />
</Routes>
</div>

View file

@ -1,4 +1,4 @@
import type { Sprite, Variant, Stats, GenerationRun, RecentVariant, VariantScore } from './types'
import type { Sprite, Variant, Stats, GenerationRun, RecentVariant, VariantScore, TerrainGrid, TerrainElevation } from './types'
const API_BASE = '/api'
@ -110,8 +110,8 @@ export async function skipSprite(spriteId: string): Promise<void> {
return requestVoid(`${API_BASE}/sprites/${spriteId}/skip`, { method: 'POST' })
}
export async function rejectVariant(variantId: number): Promise<void> {
return requestVoid(`${API_BASE}/variants/${variantId}/reject`, { method: 'POST' })
export async function rejectVariant(variantId: number, reason?: string): Promise<void> {
return requestVoid(`${API_BASE}/variants/${variantId}/reject`, jsonPost({ reason: reason ?? null }))
}
export async function regenerateSprite(spriteId: string, prompt?: string, dimensionId?: number): Promise<void> {
@ -246,6 +246,7 @@ export async function runPipeline(params?: {
scorers?: string[]
skip_process?: boolean
score_only?: boolean
rescore?: boolean
}): Promise<{ status: string; pid?: number }> {
return request<{ status: string; pid?: number }>(
`${API_BASE}/pipeline/run`,
@ -265,3 +266,7 @@ export async function fetchPipelineStatus(): Promise<{
export async function fetchPipeline(): Promise<PipelineState> {
return request<PipelineState>(buildUrl('/pipeline'))
}
export async function fetchTerrainGrid(elevation: TerrainElevation): Promise<TerrainGrid> {
return request<TerrainGrid>(buildUrl('/terrain-grid', { elevation }))
}

View file

@ -80,6 +80,14 @@ export interface RecentVariant {
is_approved: boolean
scored_by: string | null
review_tier: number | null
/** JSON string of per-dimension quality scores from best passing scorer tier. */
quality_json: string | null
/** JSON string of gate results from best passing scorer tier. */
gates_json: string | null
/** Name of the scorer that produced quality_json/gates_json. */
quality_scorer: string | null
/** Reason provided when this variant was rejected. */
reject_reason: string | null
}
export interface TheaterPage {
@ -87,6 +95,26 @@ export interface TheaterPage {
total: number
}
export type TerrainElevation = 'lowland' | 'highland' | 'alpine'
export interface TerrainGridCell {
temp: number
moist: number
elevation: TerrainElevation
sprite_id: string
entity_id: string
status: 'approved' | 'review' | 'needed' | 'generating' | 'missing'
variant_id: number | null
raw_path: string | null
processed_path: string | null
is_approved: boolean
variant_count: number
}
export interface TerrainGrid {
cells: TerrainGridCell[]
}
export interface VariantScore {
id: number
variant_id: number