fullseye

Fullseye Studio — UX & design pass (v15)

A usability + visual-design overhaul of studio.py, done from a product-design perspective. This documents what changed, why (the design principles behind each choice), and how to keep improving it. Everything below is verifiable: the logic is Qt-free and unit-tested (tests/test_studio.py), and the window is built head­lessly in the suite.

Design system (derived from the brand mark)

The palette is taken from the Fullseye icon (assets/fullseye.ico) so the app and its mark read as one product — a deliberate move for brand cohesion:

role token value used for
ground NAVY_0 #14161d window background
surface NAVY_1 #1b1e28 section cards (QGroupBox)
input NAVY_2 #232734 fields, buttons, list rows
hairline LINE #2c313f borders, separators
text / muted TEXT/MUTED #e2e5ec / #8b91a0 primary / secondary text
primary accent TEAL #17b8a6 interaction, selection, focus, “Run all”
secondary accent AMBER #f5a524 section titles, knob handles, brand title
on-accent ink INK #0c1116 text on teal/amber fills

Principles applied:

Usability improvements

1. Keyboard-first — a menu bar with shortcuts

HDevelop users work by keyboard. Every action is now a QAction with a shortcut, grouped into File / Edit / View / Run / Help menus (and mirrored on the toolbar and buttons — same handler, three entry points).

action shortcut action shortcut
Open image Ctrl+O Remove stage Del
Synthetic demo Ctrl+D Move stage up / down Ctrl+↑ / Ctrl+↓
Save result Ctrl+S Clear pipeline Ctrl+Shift+⌫
Open / Save pipeline Ctrl+Shift+O/S Zoom in / out Ctrl+= / Ctrl+-
Export Ctrl+E Fit / 1:1 Ctrl+0 / Ctrl+1
Quit Ctrl+Q 3-D surface Ctrl+3
Reset / Step / Run all Home / Ctrl+→ / Ctrl+Enter    

Principle — recognition over recall + Fitts’s law: the highest-frequency actions are also large toolbar targets; everything is discoverable in a menu without hunting through panels. Help ▸ Keyboard shortcuts (F1) shows the whole table in-app, so the bindings are self-documenting.

2. Status bar for feedback

Transient results (frame B loaded, perception errors, “pipeline cleared”) now go to a status bar via win._flash(msg) instead of overwriting the pixel read-out. The read-out (x,y,value on hover) has a permanent home there too. Principle — visibility of system status; one consistent place for ephemeral messages.

3. Titled section cards (QGroupBox)

The three panels are now grouped into labelled cards — Operators, Pipeline, Selected stage · knobs, Export & I/O, Image, Display & perception (v14), Analysis — with amber uppercase titles. Principle — Gestalt grouping + progressive disclosure: structure is read at a glance instead of a flat stack of unlabelled widgets.

4. Discoverability & affordances

5. Identity

The window carries the Fullseye icon (title bar / taskbar); the toolbar shows the mark + wordmark; an About dialog (Help menu) presents the icon, version and a one-line description. A desktop shortcut (Fullseye Studio.lnk, launched via pyw.exe so no console window appears) rounds out the install-level polish.

Before → after

  before after
chrome a single coral header label menu bar + branded toolbar + status bar
keyboard none 20 actions with shortcuts
grouping flat stacks of labels + widgets 7 titled section cards
feedback a label buried in the right panel status bar + flash()
discoverability op label name [in→out] only tooltips + selected-stage detail
palette one coral accent brand teal + amber system, full state styling
identity none window/taskbar icon, toolbar mark, About, desktop shortcut

Review round (honest DoD)

The video/perception code went through a 6-agent adversarial review (findings verified against the real code, then fixed). The Studio redesign was then given a second-opinion pass by an external model (Codex, read-only), per the project’s “verify with a second AI” rule — it caught two real behaviour bugs this pass had introduced/left, both now fixed and regression-tested:

Lesson (again): an independent reviewer catches state-consistency bugs that the author’s own tests, written to the author’s mental model, miss.

How to keep improving (backlog / methods)

Ranked by usability payoff; each is a self-contained, testable increment:

  1. Drag-to-reorder stages (done this pass) — the pipeline list is now QListWidget.InternalMove; a drop permutes model.stages via each row’s stored model index. Up/Down buttons and Ctrl+↑/↓ still work.
  2. Per-op knob semantics. The two knobs mean different things per op; surface the actual meaning (min/max radius, threshold, sigma) by adding an optional knob_doc to the Op dataclass and showing it under the sliders.
  3. Live thumbnails of each stage’s result in the pipeline rows (QListWidget item icons) — turns the list into a visual filmstrip.
  4. Recent files / recent pipelines menu (QSettings), and remember window geometry + splitter sizes between sessions.
  5. Non-blocking apply for slow ops (QThreadPool) with a busy indicator, so knob dragging stays responsive on large images.
  6. Empty/So-what states (done this pass) — a scalar-feature, contour, or no-image result now shows a centred message in the image area (via ImageView.set_message) instead of a blank view.
  7. Command palette (Ctrl+P) (done this pass) — a fuzzy-filtered dialog to run any action or insert any operator by name (palette_filter is a Qt-free, unit-tested ranker: prefix > word-start > substring).
  8. Onboarding: a first-run overlay pointing at the operator browser → pipeline → knobs → result loop.

Method for each: keep the logic in a Qt-free helper (like PipelineModel / PerceptionModel / op_detail), unit-test the helper, and let the widget be a thin wiring layer — the pattern that makes this file testable head­lessly.