diff --git a/tools/sprite-generation/gui/src/App.tsx b/tools/sprite-generation/gui/src/App.tsx
index fbcfa5a1..579dfddd 100644
--- a/tools/sprite-generation/gui/src/App.tsx
+++ b/tools/sprite-generation/gui/src/App.tsx
@@ -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 {
} />
} />
} />
+ } />
} />
diff --git a/tools/sprite-generation/gui/src/api.ts b/tools/sprite-generation/gui/src/api.ts
index 05ef78bd..db4f6f11 100644
--- a/tools/sprite-generation/gui/src/api.ts
+++ b/tools/sprite-generation/gui/src/api.ts
@@ -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 {
return requestVoid(`${API_BASE}/sprites/${spriteId}/skip`, { method: 'POST' })
}
-export async function rejectVariant(variantId: number): Promise {
- return requestVoid(`${API_BASE}/variants/${variantId}/reject`, { method: 'POST' })
+export async function rejectVariant(variantId: number, reason?: string): Promise {
+ return requestVoid(`${API_BASE}/variants/${variantId}/reject`, jsonPost({ reason: reason ?? null }))
}
export async function regenerateSprite(spriteId: string, prompt?: string, dimensionId?: number): Promise {
@@ -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 {
return request(buildUrl('/pipeline'))
}
+
+export async function fetchTerrainGrid(elevation: TerrainElevation): Promise {
+ return request(buildUrl('/terrain-grid', { elevation }))
+}
diff --git a/tools/sprite-generation/gui/src/types.ts b/tools/sprite-generation/gui/src/types.ts
index b0aba56e..cfa34c82 100644
--- a/tools/sprite-generation/gui/src/types.ts
+++ b/tools/sprite-generation/gui/src/types.ts
@@ -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