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 @@
-
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));
}