diff --git a/frontend/src/app.ts b/frontend/src/app.ts index 863292c..ff0c94f 100644 --- a/frontend/src/app.ts +++ b/frontend/src/app.ts @@ -140,7 +140,6 @@ type AppState = { cleanupImage: ImageAsset | null; heatmapImage: ImageAsset | null; finalOutputImage: ImageAsset | null; - runSummary: JsonRecord | null; runtimeConfig: RuntimeConfig | null; queueSummary: QueueSummary | null; queuePosition: number | null; @@ -201,8 +200,7 @@ const gridToggle = byId("gridToggle"); const editorCanvas = byId("editorCanvas"); const editorGridCanvas = byId("editorGridCanvas"); const editorSurface = byId("editorSurface"); -const editorMeta = byId("editorMeta"); -const summaryPanel = byId("summaryPanel"); +const editorCanvasSizeValue = byId("editorCanvasSizeValue"); const resetEditorButton = byId("resetEditorButton"); const downloadButton = byId("downloadButton"); @@ -277,7 +275,6 @@ const state: AppState = { cleanupImage: null, heatmapImage: null, finalOutputImage: null, - runSummary: null, runtimeConfig: null, queueSummary: null, queuePosition: null, @@ -524,7 +521,6 @@ function resetRunArtifacts(options: { preserveSourceImage?: boolean } = {}): voi state.cleanupImage = null; state.heatmapImage = null; state.finalOutputImage = null; - state.runSummary = null; state.queuePosition = null; state.queueDepth = 0; state.waitingCount = 0; @@ -670,17 +666,6 @@ async function fileToImageAsset(file: File): Promise { }; } -function readNestedNumber(root: JsonRecord | null, path: string[]): number | null { - let current: unknown = root; - for (const key of path) { - if (!current || typeof current !== "object" || !(key in current)) { - return null; - } - current = (current as JsonRecord)[key]; - } - return typeof current === "number" ? current : null; -} - function formatNumber(value: number | null, digits = 3): string { if (value === null || !Number.isFinite(value)) { return "n/a"; @@ -858,27 +843,6 @@ function renderStatusMetrics(): void { renderStatusMetricItems(items); } -function renderSummary(): void { - summaryPanel.innerHTML = ""; - if (!state.runSummary) { - summaryPanel.innerHTML = `

Final metrics show up here after the run finishes.

`; - return; - } - const entries: Array<[string, number | null]> = [ - ["Structure score", readNestedNumber(state.runSummary, ["source_structure", "score"])], - ["Edge F1", readNestedNumber(state.runSummary, ["source_structure", "edge_f1"])], - ["Exact match", readNestedNumber(state.runSummary, ["source_structure", "exact_match_ratio"])], - ["Final fidelity", readNestedNumber(state.runSummary, ["source_fidelity", "final_output", "score"])], - ["Mean displacement", readNestedNumber(state.runSummary, ["optimizer_displacement", "final_output", "mean_magnitude_px"])], - ]; - for (const [label, value] of entries) { - const card = document.createElement("div"); - card.className = "info-card summary-card"; - card.innerHTML = `${label}${formatNumber(value, 3)}`; - summaryPanel.appendChild(card); - } -} - function resolveViewerPanels(): Record { const frame = getSelectedFrame(); const debugViewerStage = new Set(["inference", "analysis", "selection", "rerank", "solver", "cleanup"]); @@ -1199,7 +1163,7 @@ function renderEditor(): void { editorCanvas.height = 1; editorGridCanvas.width = 1; editorGridCanvas.height = 1; - editorMeta.innerHTML = `

Final output lands here once the solver finishes its little pilgrimage.

`; + editorCanvasSizeValue.textContent = "n/a"; return; } const width = offscreenCanvas.width * state.zoom; @@ -1233,17 +1197,7 @@ function renderEditor(): void { gridContext.stroke(); } } - - editorMeta.innerHTML = ` -
- Canvas - ${offscreenCanvas.width} x ${offscreenCanvas.height} -
-
- State - ${state.editorDirty ? "Edited" : "Matches solver output"} -
- `; + editorCanvasSizeValue.textContent = `${offscreenCanvas.width} x ${offscreenCanvas.height}`; } function setZoom(nextZoom: number, anchor: ViewportAnchor | null = null): void { @@ -1460,9 +1414,6 @@ function applyJobSnapshot(snapshot: Partial): void { if (typeof snapshot.status === "string") { setJobState(snapshot.status); } - if (snapshot.runSummary && typeof snapshot.runSummary === "object") { - state.runSummary = snapshot.runSummary; - } } async function sendHeartbeat(): Promise { @@ -1571,7 +1522,6 @@ async function renderEverything(): Promise { renderInference(); renderStatusMetrics(); renderLossChart(); - renderSummary(); await renderViewer(); } @@ -1813,7 +1763,6 @@ async function handleEvent(eventName: string, payload: JsonRecord): PromiseFinal metrics show up here after the run finishes.

`; - return; - } - const entries = [ - ["Structure score", readNestedNumber(state.runSummary, ["source_structure", "score"])], - ["Edge F1", readNestedNumber(state.runSummary, ["source_structure", "edge_f1"])], - ["Exact match", readNestedNumber(state.runSummary, ["source_structure", "exact_match_ratio"])], - ["Final fidelity", readNestedNumber(state.runSummary, ["source_fidelity", "final_output", "score"])], - ["Mean displacement", readNestedNumber(state.runSummary, ["optimizer_displacement", "final_output", "mean_magnitude_px"])], - ]; - for (const [label, value] of entries) { - const card = document.createElement("div"); - card.className = "info-card summary-card"; - card.innerHTML = `${label}${formatNumber(value, 3)}`; - summaryPanel.appendChild(card); - } -} function resolveViewerPanels() { const frame = getSelectedFrame(); const debugViewerStage = new Set(["inference", "analysis", "selection", "rerank", "solver", "cleanup"]); @@ -949,7 +916,7 @@ function renderEditor() { editorCanvas.height = 1; editorGridCanvas.width = 1; editorGridCanvas.height = 1; - editorMeta.innerHTML = `

Final output lands here once the solver finishes its little pilgrimage.

`; + editorCanvasSizeValue.textContent = "n/a"; return; } const width = offscreenCanvas.width * state.zoom; @@ -982,16 +949,7 @@ function renderEditor() { gridContext.stroke(); } } - editorMeta.innerHTML = ` -
- Canvas - ${offscreenCanvas.width} x ${offscreenCanvas.height} -
-
- State - ${state.editorDirty ? "Edited" : "Matches solver output"} -
- `; + editorCanvasSizeValue.textContent = `${offscreenCanvas.width} x ${offscreenCanvas.height}`; } function setZoom(nextZoom, anchor = null) { const clampedZoom = clampZoom(nextZoom); @@ -1189,9 +1147,6 @@ function applyJobSnapshot(snapshot) { if (typeof snapshot.status === "string") { setJobState(snapshot.status); } - if (snapshot.runSummary && typeof snapshot.runSummary === "object") { - state.runSummary = snapshot.runSummary; - } } async function sendHeartbeat() { if (!state.jobId || !hasActiveJob()) { @@ -1295,7 +1250,6 @@ async function renderEverything() { renderInference(); renderStatusMetrics(); renderLossChart(); - renderSummary(); await renderViewer(); } async function handleEvent(eventName, payload) { @@ -1498,7 +1452,6 @@ async function handleEvent(eventName, payload) { break; case "pipeline_completed": state.finalOutputImage = payload.outputImage; - state.runSummary = payload.runSummary ?? null; stopHeartbeat(); setJobState("completed"); setStage("completed", "Run complete", "The output is ready. If the machine still did something stupid, fix it pixel by pixel."); diff --git a/src/repixelizer/gui_static/index.html b/src/repixelizer/gui_static/index.html index 749c687..22bfb7e 100644 --- a/src/repixelizer/gui_static/index.html +++ b/src/repixelizer/gui_static/index.html @@ -97,8 +97,14 @@

Pencil the stragglers. Wheel zooms to cursor, middle mouse pans, Alt steals color.

- - +
+ Canvas + n/a +
+
+ + +
@@ -140,12 +146,6 @@ -
-
-

Final output lands here once the solver finishes its little pilgrimage.

-
-
-
diff --git a/src/repixelizer/gui_static/styles.css b/src/repixelizer/gui_static/styles.css index dc4d1a5..715eacc 100644 --- a/src/repixelizer/gui_static/styles.css +++ b/src/repixelizer/gui_static/styles.css @@ -191,8 +191,6 @@ p { .sidebar, .workspace, .sidebar-section, -.editor-meta, -.summary-panel, .candidate-list, .event-log, .info-stack { @@ -207,7 +205,6 @@ p { .sidebar, .sidebar-section, -.summary-panel, .candidate-list, .event-log, .info-stack, @@ -865,6 +862,25 @@ select.field-control optgroup { gap: 10px; flex-wrap: wrap; justify-content: flex-end; + margin-left: auto; +} + +.editor-button-group { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + justify-content: flex-end; +} + +.editor-canvas-card { + min-width: 148px; + padding: 10px 12px; +} + +.editor-canvas-card span { + color: var(--ui-text-bright); + font-variant-numeric: tabular-nums; } .editor-toolbar { @@ -968,10 +984,7 @@ select.field-control optgroup { } .editor-stage { - display: grid; - grid-template-columns: minmax(0, 1fr) 290px; - gap: 12px; - align-items: start; + display: block; } .checkerboard { @@ -1011,10 +1024,6 @@ select.field-control optgroup { pointer-events: none; } -.summary-panel { - align-content: start; -} - input[type="range"] { width: 100%; accent-color: var(--ui-accent); @@ -1095,6 +1104,10 @@ select:focus-visible { width: 100%; } + .editor-button-group { + width: 100%; + } + .status-metrics { grid-template-columns: repeat(var(--status-metric-count), minmax(88px, 1fr)); }