From 96a5978a329da45e367e2ec417bfa845111d4bbd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 5 Jun 2026 22:44:05 +0100 Subject: [PATCH] Expand Repixelizer Eve product surface --- docs/fixtures/repixelizer.eve-surface.json | 693 ++++++++++++++++++++- src/repixelizer/eve_surface.py | 214 +++++++ 2 files changed, 906 insertions(+), 1 deletion(-) diff --git a/docs/fixtures/repixelizer.eve-surface.json b/docs/fixtures/repixelizer.eve-surface.json index ce73297..2326191 100644 --- a/docs/fixtures/repixelizer.eve-surface.json +++ b/docs/fixtures/repixelizer.eve-surface.json @@ -165,6 +165,697 @@ }, "text": "Repixelizer hosted demo" }, + { + "children": [ + { + "children": [], + "id": "repixelizer.product.pitch", + "kind": "text", + "props": { + "role": "strong", + "text": "You need pixel art for a project, but pixel art is a craft and your favorite AI is mostly vibes in a trench coat." + }, + "role": "strong", + "text": "You need pixel art for a project, but pixel art is a craft and your favorite AI is mostly vibes in a trench coat." + }, + { + "children": [ + { + "children": [ + { + "children": [], + "id": "repixelizer.product.input.image", + "kind": "image.preview", + "props": { + "aspectRatio": "1/1", + "imageRendering": "pixelated", + "label": "AI-generated hazmat character before repixelizing", + "role": "body", + "src": "https://repixelizer.gamecult.org/app/landing-assets/character-input.png" + }, + "role": "body", + "text": "AI-generated hazmat character before repixelizing" + }, + { + "children": [], + "id": "repixelizer.product.input.copy", + "kind": "text", + "props": { + "role": "body", + "text": "Looks charming from orbit. Suspiciously charming." + }, + "role": "body", + "text": "Looks charming from orbit. Suspiciously charming." + } + ], + "id": "repixelizer.product.input", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "The AI gives you this" + }, + "role": "group", + "style": { + "tone": "cool", + "variant": "repixelizer-retro-card" + }, + "text": "The AI gives you this" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.product.closeup.image", + "kind": "image.preview", + "props": { + "aspectRatio": "1/1", + "crop": "42% 63%", + "imageRendering": "pixelated", + "label": "Zoomed close-up showing fake pixel-art artifacts", + "role": "body", + "src": "https://repixelizer.gamecult.org/app/landing-assets/character-input.png", + "zoom": 3.45 + }, + "role": "body", + "text": "Zoomed close-up showing fake pixel-art artifacts" + }, + { + "children": [], + "id": "repixelizer.product.closeup.copy", + "kind": "text", + "props": { + "role": "body", + "text": "Smears, half-cells, anti-grid nonsense. The crime scene has opinions." + }, + "role": "body", + "text": "Smears, half-cells, anti-grid nonsense. The crime scene has opinions." + } + ], + "id": "repixelizer.product.closeup", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Then you zoom in" + }, + "role": "group", + "style": { + "tone": "cool", + "variant": "repixelizer-retro-card" + }, + "text": "Then you zoom in" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.product.output.image", + "kind": "image.preview", + "props": { + "aspectRatio": "1/1", + "imageRendering": "pixelated", + "label": "Repixelized hazmat character output", + "role": "body", + "src": "https://repixelizer.gamecult.org/app/landing-assets/character-repixelized.png" + }, + "role": "body", + "text": "Repixelized hazmat character output" + }, + { + "children": [], + "id": "repixelizer.product.output.copy", + "kind": "text", + "props": { + "role": "body", + "text": "Real cells on a real lattice, ready for cleanup or export." + }, + "role": "body", + "text": "Real cells on a real lattice, ready for cleanup or export." + } + ], + "id": "repixelizer.product.output", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Repixelizer spits out this" + }, + "role": "group", + "style": { + "tone": "cool", + "variant": "repixelizer-retro-card" + }, + "text": "Repixelizer spits out this" + } + ], + "id": "repixelizer.product.gallery", + "kind": "partition", + "layout": { + "direction": "horizontal", + "gap": 12 + }, + "props": { + "gap": 12, + "role": "product.gallery", + "split": "x" + }, + "role": "gallery" + } + ], + "id": "repixelizer.product.landing", + "kind": "pane", + "layout": { + "direction": "vertical", + "padding": 10 + }, + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Hosted demo" + }, + "role": "section", + "style": { + "tone": "warm", + "variant": "repixelizer-retro-pane" + }, + "text": "Hosted demo" + }, + { + "children": [ + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.status", + "kind": "status.stage", + "props": { + "detail": "Choose a file, then run the machine.", + "label": "Idle", + "role": "body", + "stage": "Waiting for input" + }, + "role": "body", + "text": "Idle" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.input.canvas", + "kind": "canvas.preview", + "props": { + "label": "Input", + "role": "body", + "state": "empty" + }, + "role": "body", + "text": "Input" + }, + { + "children": [], + "id": "repixelizer.workspace.output.canvas", + "kind": "canvas.preview", + "props": { + "label": "Output", + "role": "body", + "state": "empty" + }, + "role": "body", + "text": "Output" + } + ], + "id": "repixelizer.workspace.compare", + "kind": "partition", + "layout": { + "direction": "horizontal", + "gap": 10 + }, + "props": { + "gap": 10, + "role": "comparison", + "split": "x" + }, + "role": "comparison" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.inspect.copy", + "kind": "text", + "props": { + "role": "body", + "text": "Hold either preview to inspect both panels together." + }, + "role": "body", + "text": "Hold either preview to inspect both panels together." + }, + { + "children": [], + "id": "repixelizer.workspace.inspect.zoom", + "kind": "input.select", + "props": { + "label": "Inspect Zoom", + "role": "field", + "value": "8x" + }, + "role": "field", + "text": "Inspect Zoom" + } + ], + "id": "repixelizer.workspace.inspect", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Zoom" + }, + "role": "group", + "style": { + "tone": "panel", + "variant": "repixelizer-retro-card" + }, + "text": "Zoom" + } + ], + "id": "repixelizer.workspace.viewer", + "kind": "pane", + "layout": { + "direction": "vertical", + "padding": 10 + }, + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Pipeline Stages" + }, + "role": "section", + "style": { + "tone": "warm", + "variant": "repixelizer-retro-pane" + }, + "text": "Pipeline Stages" + }, + { + "children": [ + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.reset", + "kind": "control.button", + "props": { + "commandId": "repixelizer.editor.reset", + "label": "Reset Output", + "role": "command" + }, + "role": "command", + "text": "Reset Output" + }, + { + "children": [], + "id": "repixelizer.workspace.export", + "kind": "control.button", + "props": { + "commandId": "repixelizer.editor.export", + "label": "Export PNG", + "role": "command" + }, + "role": "command", + "text": "Export PNG" + } + ], + "id": "repixelizer.workspace.editor.actions", + "kind": "partition", + "layout": { + "direction": "horizontal", + "gap": 8 + }, + "props": { + "gap": 8, + "role": "toolbar", + "split": "x" + }, + "role": "toolbar" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.swatch", + "kind": "color.swatch", + "props": { + "label": "Paint", + "role": "body", + "value": "#ffffffff" + }, + "role": "body", + "text": "Paint" + }, + { + "children": [], + "id": "repixelizer.workspace.paint.r", + "kind": "input.number", + "props": { + "label": "R", + "role": "field", + "value": "255" + }, + "role": "field", + "text": "R" + }, + { + "children": [], + "id": "repixelizer.workspace.paint.g", + "kind": "input.number", + "props": { + "label": "G", + "role": "field", + "value": "255" + }, + "role": "field", + "text": "G" + }, + { + "children": [], + "id": "repixelizer.workspace.paint.b", + "kind": "input.number", + "props": { + "label": "B", + "role": "field", + "value": "255" + }, + "role": "field", + "text": "B" + }, + { + "children": [], + "id": "repixelizer.workspace.paint.a", + "kind": "input.number", + "props": { + "label": "A", + "role": "field", + "value": "255" + }, + "role": "field", + "text": "A" + }, + { + "children": [], + "id": "repixelizer.workspace.zoom", + "kind": "control.range", + "props": { + "label": "Zoom", + "max": 32, + "min": 4, + "role": "field", + "value": "12x" + }, + "role": "field", + "text": "Zoom" + }, + { + "children": [], + "id": "repixelizer.workspace.grid", + "kind": "control.toggle", + "props": { + "label": "Grid", + "role": "body", + "value": true + }, + "role": "body", + "text": "Grid" + } + ], + "id": "repixelizer.workspace.paint", + "kind": "partition", + "layout": { + "direction": "horizontal", + "gap": 6 + }, + "props": { + "gap": 6, + "role": "paint-controls", + "split": "x" + }, + "role": "paint-controls" + }, + { + "children": [], + "id": "repixelizer.workspace.editor.canvas", + "kind": "canvas.editor", + "props": { + "label": "Editable output canvas", + "role": "body", + "state": "empty" + }, + "role": "body", + "text": "Editable output canvas" + } + ], + "id": "repixelizer.workspace.editor", + "kind": "pane", + "layout": { + "direction": "vertical", + "padding": 10 + }, + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Pixel Fixes" + }, + "role": "section", + "style": { + "tone": "warm", + "variant": "repixelizer-retro-pane" + }, + "text": "Pixel Fixes" + }, + { + "children": [ + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.input.copy", + "kind": "text", + "props": { + "role": "body", + "text": "Start with the ugly input." + }, + "role": "body", + "text": "Start with the ugly input." + }, + { + "children": [], + "id": "repixelizer.workspace.file", + "kind": "input.file", + "props": { + "accept": "image/png,image/*", + "label": "Choose File", + "role": "body" + }, + "role": "body", + "text": "Choose File" + }, + { + "children": [], + "id": "repixelizer.workspace.dropzone", + "kind": "dropzone", + "props": { + "label": "PNG, sprite scrap, cursed logo. Whatever started this.", + "role": "body" + }, + "role": "body", + "text": "PNG, sprite scrap, cursed logo. Whatever started this." + } + ], + "id": "repixelizer.workspace.input", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Input" + }, + "role": "group", + "style": { + "tone": "panel", + "variant": "repixelizer-retro-card" + }, + "text": "Input" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.target.size", + "kind": "input.number", + "props": { + "label": "Target Size", + "role": "field", + "value": "auto" + }, + "role": "field", + "text": "Target Size" + }, + { + "children": [], + "id": "repixelizer.workspace.target.width", + "kind": "input.number", + "props": { + "label": "Target Width", + "role": "field", + "value": "auto" + }, + "role": "field", + "text": "Target Width" + }, + { + "children": [], + "id": "repixelizer.workspace.target.height", + "kind": "input.number", + "props": { + "label": "Target Height", + "role": "field", + "value": "auto" + }, + "role": "field", + "text": "Target Height" + }, + { + "children": [], + "id": "repixelizer.workspace.steps", + "kind": "input.number", + "props": { + "label": "Steps", + "role": "field", + "value": "48" + }, + "role": "field", + "text": "Steps" + }, + { + "children": [], + "id": "repixelizer.workspace.run", + "kind": "control.button", + "props": { + "commandId": "repixelizer.job.submit", + "label": "Run The Machine", + "method": "POST", + "role": "command", + "route": "/api/jobs" + }, + "role": "command", + "text": "Run The Machine" + } + ], + "id": "repixelizer.workspace.controls", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Controls" + }, + "role": "group", + "style": { + "tone": "panel", + "variant": "repixelizer-retro-card" + }, + "text": "Controls" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.queue.depth", + "kind": "metric", + "props": { + "label": "Queue", + "max": 32, + "role": "body", + "value": 0 + }, + "role": "body", + "text": "Queue" + }, + { + "children": [], + "id": "repixelizer.workspace.queue.worker", + "kind": "metric", + "props": { + "label": "Worker", + "max": 1, + "role": "body", + "value": 0 + }, + "role": "body", + "text": "Worker" + } + ], + "id": "repixelizer.workspace.queue", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Queue" + }, + "role": "group", + "style": { + "tone": "panel", + "variant": "repixelizer-retro-card" + }, + "text": "Queue" + }, + { + "children": [ + { + "children": [], + "id": "repixelizer.workspace.lattice.copy", + "kind": "text", + "props": { + "role": "body", + "text": "No lattice picked yet." + }, + "role": "body", + "text": "No lattice picked yet." + } + ], + "id": "repixelizer.workspace.lattice", + "kind": "card", + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Lattice" + }, + "role": "group", + "style": { + "tone": "panel", + "variant": "repixelizer-retro-card" + }, + "text": "Lattice" + } + ], + "id": "repixelizer.workspace.sidebar", + "kind": "pane", + "layout": { + "direction": "vertical", + "padding": 10 + }, + "props": { + "styleProfile": "repixelizer.retro.pixel", + "title": "Run Controls" + }, + "role": "section", + "style": { + "tone": "warm", + "variant": "repixelizer-retro-pane" + }, + "text": "Run Controls" + } + ], + "id": "repixelizer.workspace", + "kind": "partition", + "layout": { + "direction": "horizontal", + "gap": 12 + }, + "props": { + "gap": 12, + "role": "repixelizer.workspace", + "split": "x" + }, + "role": "workspace" + }, { "children": [ { @@ -553,6 +1244,6 @@ }, "title": "Repixelizer", "type": "surface-state", - "updatedAt": "2026-06-04T00:00:00Z", + "updatedAt": "2026-06-05T00:00:00Z", "version": 1 } diff --git a/src/repixelizer/eve_surface.py b/src/repixelizer/eve_surface.py index 79c0974..b03e431 100644 --- a/src/repixelizer/eve_surface.py +++ b/src/repixelizer/eve_surface.py @@ -44,6 +44,28 @@ def _card(element_id: str, title: str, children: list[dict[str, Any]], *, tone: } +def _leaf(element_id: str, kind: str, *, text: str | None = None, role: str = "body", **props: Any) -> dict[str, Any]: + payload = {"text": text, "role": role, **props} + if text is None: + payload.pop("text") + return { + "id": element_id, + "kind": kind, + "role": role, + "text": text or props.get("label") or props.get("title") or element_id, + "props": payload, + "children": [], + } + + +def _button(element_id: str, label: str, *, command_id: str, **props: Any) -> dict[str, Any]: + return _leaf(element_id, "control.button", role="command", label=label, commandId=command_id, **props) + + +def _field(element_id: str, label: str, *, value: str, field_kind: str = "input.number", **props: Any) -> dict[str, Any]: + return _leaf(element_id, field_kind, role="field", label=label, value=value, **props) + + def _pane(element_id: str, title: str, children: list[dict[str, Any]], *, direction: str = "vertical") -> dict[str, Any]: return { "id": element_id, @@ -177,6 +199,196 @@ def _runtime_cards( ] +def _landing_gallery(base: str) -> dict[str, Any]: + return _pane( + "repixelizer.product.landing", + "Hosted demo", + [ + _text( + "repixelizer.product.pitch", + "You need pixel art for a project, but pixel art is a craft and your favorite AI is mostly vibes in a trench coat.", + role="strong", + ), + { + "id": "repixelizer.product.gallery", + "kind": "partition", + "role": "gallery", + "layout": {"direction": "horizontal", "gap": 12}, + "props": {"split": "x", "gap": 12, "role": "product.gallery"}, + "children": [ + _card( + "repixelizer.product.input", + "The AI gives you this", + [ + _leaf( + "repixelizer.product.input.image", + "image.preview", + label="AI-generated hazmat character before repixelizing", + src=f"{base}/app/landing-assets/character-input.png", + imageRendering="pixelated", + aspectRatio="1/1", + ), + _text("repixelizer.product.input.copy", "Looks charming from orbit. Suspiciously charming."), + ], + tone="cool", + ), + _card( + "repixelizer.product.closeup", + "Then you zoom in", + [ + _leaf( + "repixelizer.product.closeup.image", + "image.preview", + label="Zoomed close-up showing fake pixel-art artifacts", + src=f"{base}/app/landing-assets/character-input.png", + crop="42% 63%", + zoom=3.45, + imageRendering="pixelated", + aspectRatio="1/1", + ), + _text("repixelizer.product.closeup.copy", "Smears, half-cells, anti-grid nonsense. The crime scene has opinions."), + ], + tone="cool", + ), + _card( + "repixelizer.product.output", + "Repixelizer spits out this", + [ + _leaf( + "repixelizer.product.output.image", + "image.preview", + label="Repixelized hazmat character output", + src=f"{base}/app/landing-assets/character-repixelized.png", + imageRendering="pixelated", + aspectRatio="1/1", + ), + _text("repixelizer.product.output.copy", "Real cells on a real lattice, ready for cleanup or export."), + ], + tone="cool", + ), + ], + }, + ], + ) + + +def _workspace_surface(config: HostedDemoConfig, queue_summary: dict[str, Any]) -> dict[str, Any]: + return { + "id": "repixelizer.workspace", + "kind": "partition", + "role": "workspace", + "layout": {"direction": "horizontal", "gap": 12}, + "props": {"split": "x", "gap": 12, "role": "repixelizer.workspace"}, + "children": [ + _pane( + "repixelizer.workspace.viewer", + "Pipeline Stages", + [ + _leaf( + "repixelizer.workspace.status", + "status.stage", + label="Idle", + stage="Waiting for input", + detail="Choose a file, then run the machine.", + ), + { + "id": "repixelizer.workspace.compare", + "kind": "partition", + "role": "comparison", + "layout": {"direction": "horizontal", "gap": 10}, + "props": {"split": "x", "gap": 10, "role": "comparison"}, + "children": [ + _leaf("repixelizer.workspace.input.canvas", "canvas.preview", label="Input", state="empty"), + _leaf("repixelizer.workspace.output.canvas", "canvas.preview", label="Output", state="empty"), + ], + }, + _card( + "repixelizer.workspace.inspect", + "Zoom", + [ + _text("repixelizer.workspace.inspect.copy", "Hold either preview to inspect both panels together."), + _field("repixelizer.workspace.inspect.zoom", "Inspect Zoom", value="8x", field_kind="input.select"), + ], + ), + ], + ), + _pane( + "repixelizer.workspace.editor", + "Pixel Fixes", + [ + { + "id": "repixelizer.workspace.editor.actions", + "kind": "partition", + "role": "toolbar", + "layout": {"direction": "horizontal", "gap": 8}, + "props": {"split": "x", "gap": 8, "role": "toolbar"}, + "children": [ + _button("repixelizer.workspace.reset", "Reset Output", command_id="repixelizer.editor.reset"), + _button("repixelizer.workspace.export", "Export PNG", command_id="repixelizer.editor.export"), + ], + }, + { + "id": "repixelizer.workspace.paint", + "kind": "partition", + "role": "paint-controls", + "layout": {"direction": "horizontal", "gap": 6}, + "props": {"split": "x", "gap": 6, "role": "paint-controls"}, + "children": [ + _leaf("repixelizer.workspace.swatch", "color.swatch", label="Paint", value="#ffffffff"), + _field("repixelizer.workspace.paint.r", "R", value="255"), + _field("repixelizer.workspace.paint.g", "G", value="255"), + _field("repixelizer.workspace.paint.b", "B", value="255"), + _field("repixelizer.workspace.paint.a", "A", value="255"), + _field("repixelizer.workspace.zoom", "Zoom", value="12x", field_kind="control.range", min=4, max=32), + _leaf("repixelizer.workspace.grid", "control.toggle", label="Grid", value=True), + ], + }, + _leaf("repixelizer.workspace.editor.canvas", "canvas.editor", label="Editable output canvas", state="empty"), + ], + ), + _pane( + "repixelizer.workspace.sidebar", + "Run Controls", + [ + _card( + "repixelizer.workspace.input", + "Input", + [ + _text("repixelizer.workspace.input.copy", "Start with the ugly input."), + _leaf("repixelizer.workspace.file", "input.file", label="Choose File", accept="image/png,image/*"), + _leaf("repixelizer.workspace.dropzone", "dropzone", label="PNG, sprite scrap, cursed logo. Whatever started this."), + ], + ), + _card( + "repixelizer.workspace.controls", + "Controls", + [ + _field("repixelizer.workspace.target.size", "Target Size", value="auto"), + _field("repixelizer.workspace.target.width", "Target Width", value="auto"), + _field("repixelizer.workspace.target.height", "Target Height", value="auto"), + _field("repixelizer.workspace.steps", "Steps", value=str(config.default_steps)), + _button("repixelizer.workspace.run", "Run The Machine", command_id="repixelizer.job.submit", route="/api/jobs", method="POST"), + ], + ), + _card( + "repixelizer.workspace.queue", + "Queue", + [ + _leaf("repixelizer.workspace.queue.depth", "metric", label="Queue", value=queue_summary.get("queueDepth", 0), max=queue_summary.get("queueCapacity", config.queue_capacity)), + _leaf("repixelizer.workspace.queue.worker", "metric", label="Worker", value=1 if queue_summary.get("hasActiveJob") else 0, max=1), + ], + ), + _card( + "repixelizer.workspace.lattice", + "Lattice", + [_text("repixelizer.workspace.lattice.copy", "No lattice picked yet.")], + ), + ], + ), + ], + } + + def _lowering_comparison() -> dict[str, Any]: return { "periwinkleAndroidKotlin": { @@ -259,6 +471,8 @@ def build_repixelizer_eve_surface( ), ], ), + _landing_gallery(base), + _workspace_surface(config, queue_summary), _pane( "repixelizer.workflow", "Single page app",