akashch1512's picture
UI bettement
b7ed242
|
Raw History Blame Contribute Delete
10.5 kB

A newer version of the Gradio SDK is available: 6.31.0

Upgrade

UI review — issues and betterments

Scope: the Gradio shell (app.py) and the embedded dual-pane viewer (viewer/index.html + viewer/app.js) as it renders inside the Space. Ordered by impact. Each item names the file and the concrete change.


P0 — broken or misleading behaviour

1. Running… is never cleared when the run fails

app.py (run.click(...).then(...)) — the chain writes "Running…" into status, and predict raises gr.Error on every failure path. Gradio shows the error toast, but status is an output of the raising function, so it is never overwritten: the page keeps claiming the job is in flight, permanently.

Fix: return errors as values instead of raising for the recoverable cases (quota exhausted, no image, non-finite output), or add a trailing cleanup step that resets status regardless of outcome.

2. Stale results stay on screen during and after a new run

Uploading a second image leaves the previous run's viewer iframe, gallery, 16-bit image and download bundle visible. A user who changes the GSD and re-runs sees the old 3D scene for the whole run and has no way to tell which result the downloads belong to.

Fix: in the first .then step, clear every output (gr.update(value=None) for gallery/height/downloads, "" for the viewer) alongside "Running…".

3. The run button is not disabled while a run is in flight

Two clicks = two ZeroGPU reservations = double the quota burn, which is the single most common failure mode for this Space (see the quota error text in app.py). Nothing in the UI prevents it.

Fix: run.click(lambda: (gr.update(interactive=False), "Running…"), ...) and re-enable in a trailing .then. Setting concurrency_limit=1 on the event is worth doing too.

4. No empty state — the viewer is a 560 px void on first load

_CSS reserves the height so the layout does not collapse, but nothing is drawn there. A first-time visitor sees a large blank panel and no signal that it will become a 3D scene.

Fix: render a placeholder into gr.HTML at startup (a dashed frame with "Your 3D terrain will appear here" plus the one-line workflow), replaced on the first result.

5. No examples

There is no gr.Examples. A visitor with no aerial imagery to hand — the majority — cannot try the demo at all. viewer/sample/ already exists.

Fix: gr.Examples([[sample_png, 0.5, False]], inputs=[image, gsd, tta]) with two or three scenes at different GSDs.

6. Silent downsampling is never surfaced

read_scene(..., max_side=1600) resamples any large scene, and the viewer payload is capped again at _VIEWER_MAX_SIDE = 1024. meta.json carries the note; the UI says nothing. A user who uploads an 8000 px tile gets a quarter-resolution product and no warning.

Fix: when the input was resampled, add a line to status — "Scene resampled 8000 → 1600 px (GSD adjusted 0.15 → 0.75 m/px)."


P1 — comprehension: the result cannot be read

7. No colour legend anywhere

The turbo height colormap is the primary output — in the gallery, the 2D map pane and the 3D drape — and there is no colorbar in any of the three. Colour cannot be converted back to metres by eye. viewer/app.js has turbo(); nothing draws a scale from it.

Fix: draw a vertical colorbar with min/mid/max metre ticks into the 2D pane and the 3D pane corner, and bake one into height_colormap.png in _write_artifacts.

8. No scale bar or north arrow on the 2D map

The pane reports nothing about ground extent. S.gsd is known, so a 100 m / 500 m bar is a few lines of canvas.

9. The elevation profile is unlabelled

drawProfile in viewer/app.js prints only the max height. No y-axis minimum, no x-axis ground distance, no units on the axis. The numbers exist in reportProbe but are not tied to the plot.

Fix: label both ends of the y range and the x extent in metres.

10. The low-ground warning is buried

The frac_below_1m < 0.15 warning is appended to the bottom of a four-line markdown blob in the sidebar. It is the most important correctness signal the app produces — it usually means the declared GSD is wrong.

Fix: give it its own gr.Markdown with a visible warning style, above the stats, hidden when not triggered.

11. Two product names in one page

The Gradio header says "Single View Height Estimator"; the iframe sidebar says "DepthWizard · single-view DSM · SIH 2026 PS 26175". Inside one viewport that reads as an embedded third-party tool.

Fix: pick one name; hide or rewrite the viewer's <h1>/.tag under body.embedded.

12. Dead UI in embedded mode

The viewer hides #loadSection when embedded, but leaves:

  • the Validation section telling the user to "load gt_ndsm_m.npy alongside" — impossible, the file picker is gone;
  • permanently-disabled Reference height / Error vs reference options in both layer dropdowns.

Fix: body.embedded hides the Validation section and both reference options; keep them for the standalone viewer/index.html.

13. "Mesh detail" options are misleading in the Space

The embedded payload is capped at 1024 px (~1.05 M pixels), so "Full (1.5M)" and "Balanced (500k)" differ little and "Full" can never be reached. _VIEWER_MAX_SIDE and the #quality options are set independently and disagree.

Fix: derive the option list from the actual grid size, or relabel to Fast / Balanced / Full without vertex counts.

14. Downloads are an unlabelled pile of temp paths

gr.File(file_count="multiple") shows dw_a1b2c3_result.zip, ndsm_m.npy, terrain.glb… with no explanation of what to do with each. The zip name is a temp directory id.

Fix: rename the archive after the uploaded file, and put a one-line legend under the component — ".npy = raw metres · .glb = textured mesh · meta.json = encoding + scene metadata".


P2 — input ergonomics

15. The GSD field is the app's main footgun and is under-supported

minimum=0.01, no maximum, and 0 silently means "guess from the checkpoint's canonical GSD" (float(gsd or 0) → read_scene falls back to spec.canonical_gsd_m). Nothing tells the user that, before or after.

Fix: add a preset radio (0.3 / 0.5 / 1.0 m/px + Custom + "I don't know"), make the "don't know" path explicit, and echo the effective GSD and its source in the status line (scene_meta.gsd_source is already computed).

16. TTA does not disclose its cost

"Higher quality (slower)" hides the fact that it asks for a 120 s ZeroGPU window instead of 45 s — and that this is the usual cause of the quota error. The user only learns it from the failure message.

Fix: info="~4× the compute; reserves 120 s of GPU quota instead of 45 s".

17. No expected runtime anywhere

Nothing tells the user whether to wait 10 s or 3 minutes before the first run.

18. No reset / clear

Once a result is on screen there is no way back to the empty state.


P3 — layout, theme, accessibility

19. The Gradio shell is light, the viewer is hard-coded dark

viewer/index.html sets color-scheme: dark and --bg:#0d1013. In default (light) Gradio that is a 760 px black slab in a white page.

Fix: set a dark gr.themes on the Blocks, or make the viewer's palette follow prefers-color-scheme and pass the host theme in through the payload.

20. Fixed 760 px iframe height

Hard-coded in _viewer(). On a 768 px-tall laptop the viewer needs page scrolling, while the viewer itself is overflow:hidden — so its sidebar scrolls independently inside a frame that is already clipped. Two nested scroll contexts.

Fix: size the iframe in viewport units (height:min(760px, 78vh)) with a floor, and add a fullscreen toggle (the frame already has allowfullscreen).

21. The viewer's mobile breakpoint fires on desktop

@media (max-width:900px) is evaluated against the iframe width. In the 3:1 column split the frame is often under 900 px on a normal laptop, so the 2D pane disappears and the sidebar stacks — the mobile layout on a desktop, which reads as a bug.

Fix: switch to a container-driven breakpoint (or lower it and set a min_width on the Gradio column so the frame stays wide).

22. The 322 px fixed sidebar squeezes the 2D pane

Inside an already-narrow frame, 322px | 38% | 1fr leaves the map pane a sliver. Consider a collapsible sidebar.

23. No <label for=...> anywhere in the viewer

Every control in viewer/index.html uses a bare <label> followed by a sibling input — exaggeration, sun azimuth/elevation, drape, mesh detail. Labels are not clickable and screen readers announce nothing. The checkbox rows wrap their text in a <span>, not a label.

Fix: add for=/id= pairs, or wrap the inputs.

24. Mode buttons carry no pressed state for assistive tech

button.on is visual only — add aria-pressed. #hud, #probe and #info update silently — add aria-live="polite" to #probe and #warn.

25. gr.HTML(label="3D view") renders no label

gr.HTML ignores label. The main output panel is unheaded.

26. Keyboard shortcuts need a click first

F and R are bound on the iframe's window; until the user clicks inside the frame the host page owns the keyboard. The hint advertises them unconditionally.

Fix: mention "click the scene first", or focus the canvas on load.

27. Desktop-only interaction hints

drag = orbit · scroll = zoom with no touch equivalents, and the 2D pane probe is pointer-only with no zoom/pan.

28. warn() appends without bound or dedupe

It concatenates into #warn.innerHTML forever. A repeating error grows the sidebar indefinitely and there is no dismiss.

29. No progress feedback while the mesh builds

buildMesh() on a ~1 M-vertex grid blocks the frame for seconds after the Gradio progress bar has already finished. The user sees a frozen black pane and no spinner.

30. Small type throughout the viewer

11–12 px for controls, 11 px for .tag, #hud and #hint. Fine for a dense desktop tool, tight inside an embedded frame.


Suggested order of work

  1. P0 1–3 — state bugs; small diffs in the run.click chain.
  2. P0 4–5 — empty state + examples; the biggest first-impression win.
  3. P1 7–9 — colorbar, scale bar, profile axes; makes the output readable.
  4. P1 10–14 + P2 15–16 — messaging and input clarity.
  5. P3 — theme unification (19) and the layout/breakpoint pair (20–22) together, then accessibility (23–25).