Commit Graph
4 Commits
Author SHA1 Message Date
octoturge 9bd9728b21 Add Setup: first farm, gateway settings, add and remove printers; show alerts
A new user could sign in and see an empty page with no way forward. Setup
walks them through it: create a farm (reusing their organisation if they
already have one); get the gateway settings with a freshly minted token,
shown once and removed from the page when dismissed, with a confirmation
first if a gateway is already connected, since minting replaces the old
token; add a Bambu printer by address, serial and access code (a printer
the gateway discovered fills the form in); remove a printer behind a named
confirmation that says it also deletes the stored credential.

The access code field is cleared after every submit, success or failure, and
the page says plainly that the code goes to the gateway and nowhere else.
Add is disabled while no gateway is connected, because the code has nowhere
to go.

The fleet page now lists open alerts above the printers (polled every 5s)
with a Dismiss button, and points an empty account at Setup.

Verified in real Chrome by scripts/e2e-setup-browser.sh in continuum-backend:
nothing -> farm -> gateway -> printer -> failed-print alert -> dismissed ->
removed, with no page errors.
2026-10-29 15:15:06 +00:00
octoturge c7f85870db Add a Jobs page: upload a sliced file, watch it queue, delete it
The console could watch and control printers but not give them work. The
Jobs page uploads a .3mf/.gcode: it creates the job (POST /jobs), PUTs the
file straight to the presigned storage URL with a progress bar, and lists
the farm's jobs, polling every 5s since there is no job push channel yet.
The scheduler then starts it on the next idle printer, so there is no Start
button - a queued job IS the request.

Deliberate details: the PUT carries no Authorization header (the URL is
presigned and an uncovered header makes storage reject it) and the same
Content-Type the URL was signed for; a failed upload deletes the job row
it just created, so no queued job waits for a file that never arrives;
only queued/uploading jobs offer Delete, matching what the backend allows.

JobUpload and JobList live in @continuum/ui and take the API calls as
props, like the printer card. A shared apiErrorMessage() replaces the
inline copy on the fleet page.

Checked in real Chrome against the live stack with job dispatch OFF: upload
to the real bucket (57 bytes landed intact), listed as queued, still queued
after a scheduler tick, Delete clears it, no page errors; the test object
was removed from the bucket afterwards. pnpm -r typecheck is clean.
2026-10-29 14:39:37 +00:00
octoturge ba2eb4d379 Make the console speak the backend's telemetry protocol
The telemetry store and the printer card described a protocol no backend
ever spoke. The store expected `{ type, data: { printerId, progressPct,
state } }` with a closed five-value state enum; `/ws/telemetry` sends
`{ type: "printer.telemetry", printer: {...} }` with `id`,
`progressPercent` and an open `status`, and prefaces it with a `ready`
frame. The card also displayed `printer.model`, which the printer
resource has never had - it has `vendor`.

Authentication was the other half: the socket now connects with the
subprotocol list `["bearer", token]`, which is what the backend gates
the upgrade on, and never a query parameter. The token is read from the
session on each attempt, because supabase-js refreshes it hourly and a
reconnect replaying the first token would work right up until it
mattered. With no session it does not connect at all, and app.vue drives
connect/disconnect from the session rather than a one-shot onMounted.

Reconnection is now backed off from 1s to 30s instead of a flat 3s. A
browser cannot see why an upgrade failed - a rejected token and an
unreachable server are both just onerror then onclose - so a token the
server will never accept previously retried every 3s for as long as the
tab stayed open.

`disconnect()` no longer reconnects the socket it just closed, via a
`wanted` flag; handlers from a replaced socket are ignored.

Null is no longer rendered as zero. A claimed printer whose gateway has
never reported has no temperature, and 0 degrees is a different claim:
null readings stay out of the graph's history, the labels show a dash,
the progress bar is omitted rather than drawn empty, and a null status
reads "unknown" rather than the old "offline", which asserted something
nobody had observed. `lastSeenAt` is shown, since it is what separates
an idle printer from one nothing has heard from in an hour.

The card takes one `printer` prop instead of a row plus a `live`
overlay - the push and the REST row are the same shape by design, so
pages/index.vue replaces one with the other and @continuum/ui stays
plain Vue that knows nothing of stores or the API. Status styling now
covers all six values of the backend's printer_status enum; paused and
maintenance previously drew as ordinary text.

Types are hand-mirrored in packages/ui/src/types.ts from
continuum-schemas, the convention this project already uses across
TS and Rust. @continuum/ts-types is in another repository and is not
resolvable here; a `link:` out of the repo would break on any checkout
not laid out like this machine's. Field names are verified identical to
the schema's.

Typechecks clean (nuxt typecheck, vue-tsc). Not exercised at runtime;
the protocol it now implements is the one verified live by
continuum-backend's scripts/e2e-edge.sh.
2026-10-18 19:54:28 +00:00
octoturge 4cb2e82a92 Initial boilerplate scaffold for continuum-app 2026-08-28 16:21:32 +00:00