OpenDroneKit

The workspace cockpit

The workspace cockpit: workspaces, dockable panels, shared selection.

Updated 2026-10-02Mirrored from docs/UI_GUIDE.md at 1ef6351 · Markdown

app/web/workspace.html is the operations interface: fourteen workspaces built from one dockable framework, arranged around a canvas that keeps most of the screen.

Open it with the desktop shell (python main.py), or serve app/web/ and visit /workspace.html.

This document described the cockpit as the interface for some time while app/shell.py opened index.html instead, so the UI documented here was one no user could reach and the one they did reach was documented nowhere. python main.py now opens the cockpit.

The older single-screen shell is still there behind ODK_UI=classic, because it is the more completely wired of the two while the cockpit's workspaces are connected to the Api one at a time, and removing a working screen before its replacement is finished loses capability quietly.

What is real on screen, and what is not

The cockpit shows the structural sample until it can talk to the application. That sample is deliberately impossible to mistake for a survey: sites are named DEMO, coordinates are Null Island, clocks sit at the epoch, and the wording matches core/demo_mode.py so the desktop demo and the API demo agree about what synthetic looks like.

A banner across the top says so, and it is shown in every state except connected -- the only state in which what you are looking at came from the application. It used to be a chip at the end of the status bar, which was clipped off screen at 1600px, so the one thing declaring the data synthetic was the first thing to disappear.

?demo=1 keeps the demo even when a bridge is available, for demonstrating the product on a machine that has real projects on it.

The idea

A workspace is not a page. It is an arrangement of panels pointed at the project you are already working on. Moving from planning to flight to verification does not change the survey — it changes which instruments you are looking through, which is why the project, the selection and the coordinate system all survive the switch.

┌─ global navigation ────────────────────────────────────────────────┐
├─ contextual toolbar (changes per workspace) ───────────────────────┤
│ ┌────────┬──────────────────────────────────┬──────────────────┐   │
│ │ left   │            CANVAS                │ right            │   │
│ │ trees  │   map · 3D · image · thermal     │ properties       │   │
│ │ layers │                                  │ inspector        │   │
│ │        ├──────────────────────────────────┤ telemetry        │   │
│ │        │ bottom: timeline · logs · queue  │                  │   │
│ └────────┴──────────────────────────────────┴──────────────────┘   │
├─ status bar ───────────────────────────────────────────────────────┤

Workspaces

#WorkspaceCanvasWhat it answers
1HomeOperations mapWhat is happening across every project
2ProjectsProject extentWhat this project is and what it has produced
3Mission PlanningMission map / 3DWhat will be flown, and what it will cost
4FlightLive mapWhere the aircraft is and what it is doing
5VerificationPlanned vs actualDid we capture what we planned
6ProcessingReconstructionHow the photogrammetry is progressing
7Digital Twin3D modelWhat the asset looks like, over time
8AI InspectionImage + detection + 3DWhat the models found, and where it is
9ThermalRGB / thermal / fusedWhat is hot, and by how much
10MeasurementsOrtho / terrainHow big, how far, how much
11FleetFleet mapWhat can fly, and what needs service
12ReportsLive previewWhat the client receives
13DevelopersAPI consoleHow to integrate
14Settings—Units, CRS, models, keyboard

Using it: a run from an empty window to a report

Three things frame every workspace. The top row switches which instruments point at your project -- it does not change which project you are on. The second row is the toolbar, and it changes per workspace: that is where the verbs live. The bottom right is where every action reports. If you click something and no message appears there, that is a bug worth reporting rather than a feature you have not found.

Two habits make the panels make sense:

  1. Select first, then act. Clicking a row tells the toolbar what to work on. Cancel acts on the selected job, Accept/Reject/Flag on the selected finding, Log Maintenance on the selected aircraft. Nothing has to be typed twice.
  2. In a tree, click the label rather than the ▸. The arrow expands; the text selects.
#WorkspaceDo thisWhat happens
1ProjectsNew Project, choose a folder, name itCreated and made active
2ProjectsImport, choose a folder of imagesImported, and made the active dataset. Reselected automatically next launch
3Mission PlanningEdit altitude and overlap on the right, then PlanEach edit is confirmed, and the planner runs with those values
4Mission PlanningSave, then ExportStored in the project, then written in the flight-controller formats
5ProcessingProcess for reconstruction, or Start for the full pipelineBoth confirm first, then run as background jobs reporting progress
6ProcessingSelect a row in the queue, then CancelCancels the job you selected
7VerificationMatch Captures, select a finding, then Accept / Reject / FlagMoves its status and records who moved it
8Thermal, AI InspectionRGB, Thermal, SemanticThe canvas draws that product, or says which one has not been produced
9FleetAdd Aircraft, Add Battery, Add PilotReal rows, in the same database the web service reads
10FleetSelect an aircraft, then Log MaintenanceRecords it and resets the service clock
11ReportsGenerate ReportBuilds it, or refuses with a checklist of what is missing first
12ProjectsShareIssues a token in a blocking dialog. Copy it there: only its hash is stored

Two refusals that are working correctly

Generate Report refusing with a list is the report engine declining to emit a document with empty sections. The checklist is the useful part.

A view saying "no thermal product yet" means exactly that. It is not a broken canvas; it is a canvas with nothing true to draw, and it names what to run to produce one.

Starting it

python main.py                 # the cockpit
ODK_UI=classic python main.py  # the older single-screen shell

The UI is served over 127.0.0.1 on an ephemeral port rather than opened as a file. ES modules fetched from file:// have origin "null" and the webview refuses them, which produces a blank window and no error anywhere. Nothing leaves the machine: the server binds loopback only.

Panels

Every panel supports the same operations, because they are the same component:

  • Resize — drag the splitter between regions
  • Move — drag a panel's header into another region
  • Collapse — ▾ in the header
  • Hide — ✕, restored from Layout ▾
  • Tab-stack — panels with several views show tabs
  • Pop out — ⧉ opens the panel as its own window for a second monitor

Layout is saved per workspace and restored on return. Layout ▾ → Reset undoes it.

Saving is keyed by workspace and panel id rather than by position, so a panel added in a later release appears at its default size instead of scrambling a layout you arranged.

Selection is shared

Selecting anything publishes it to one selection bus, and every panel that cares subscribes. Select a finding in the findings table and the source image, the 3D context and the finding inspector all update — none of them knows the table exists.

That is what makes it an application rather than a set of dashboards, and it is why adding a panel never means editing another one.

Keyboard

KeyAction
Ctrl/⌘ KCommand palette — workspaces, projects, findings, commands
1 – 9Switch workspace
F11Full-screen canvas
Ctrl/⌘ BToggle side panels
FFit view

Colour means something

Colour is never decorative here. A red border always means something is wrong, or it stops meaning anything at all.

ColourMeaning
BlueInteraction, selection, the active workspace
GreenHealthy operational state
AmberWarning, degraded, needs attention
RedError, safety-critical action, serious defect
PurpleThermal and semantic visualisation only

Safety-critical actions — Abort, Land, RTL, Manual Override — are styled apart from routine ones in the toolbar, and a test asserts that rule holds. An action that stops an aircraft should never be one careless click from Save.

What it is not, yet

The status bar says sample data — not connected to a project, permanently, until a project is attached. The shell is the framework and the arrangement; the numbers in it are illustrative structure, not measurements. Nothing here should be read as a survey result, and the frame of the application says so rather than leaving you to work it out.

Toolbar actions are not wired to the API. Pressing one reports that it is not wired rather than appearing to work — a button that silently does nothing is worse than one that admits it.

The canvas regions are panel-mounted placeholders. MapLibre is already vendored in app/web/vendor/ and the existing hub uses it; mounting a real map, 3D viewport or image viewer into a canvas element is the next step and does not change the framework.

Extending it

Add a workspace by adding an object to WORKSPACES in js/workspace/workspaces.js:

const myWorkspace = {
  id: "thing", title: "Thing",
  toolbar: ["New", "|", "Process"],
  left:   [{ id: "thing.tree", title: "Items", render: () => tree([...]) }],
  canvas: () => canvas({ title: "Thing", tools: MAP_TOOLS }),
  right:  [{ id: "thing.props", title: "Properties", render: () => properties([...]) }],
  bottom: [{ id: "thing.log", title: "Log", render: () => consoleView([...]) }],
};

Nothing else changes: navigation, the toolbar, docking, persistence, the palette and the selection bus all pick it up. tests/test_workspace_ui.py will then execute it — every workspace is mounted through the real dock in Node and every panel rendered, because a panel that throws disappears silently and leaves a gap where telemetry should be.

This page is the repository’s own docs/UI_GUIDE.md, rendered at build time from commit 1ef6351. Links to code open on GitHub at the same commit.