Skip to content

feat(ui,desktop): explain a red connection dot, follow the project in use, normalize OSC loopback - #92

Merged
pyramation merged 1 commit into
mainfrom
feat/connection-diagnostics
Aug 9, 2026
Merged

feat(ui,desktop): explain a red connection dot, follow the project in use, normalize OSC loopback#92
pyramation merged 1 commit into
mainfrom
feat/connection-diagnostics

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Three failure modes that were all silent: a red dot with no reason, a "running" show with nothing listening, and OSC packets dropped into the void.

The dot now diagnoses itself. useSocket threw the close code away, never retried, and rendered one boolean. It now probes the two endpoints that separate the possible causes and reconnects with backoff:

/api/config unreachable  → serverUnreachable  "Can't reach the server — the show may be stopped."
/api/config !ok          → serverUnreachable  "HTTP <status>"
/api/me 401 (or no token)→ sessionExpired     "sign in again"
both ok, code 1000/1001  → closed             (server restarting)
both ok, any other code  → rejected           (JWT secret changed)

retryDelay(n) = min(500 * 2^(n-1), 10_000)

Amber while connecting/reconnecting, red once down, and the reason renders next to it. StatusDot is the only UI addition — the dot's position, size and colours are unchanged.

Startup failures now reach the operator. startServer only called listen, which is async, so a port clash produced a show that reported running while nothing was bound — the actual cause of a red dot with no explanation. ServerHandle gains ready, the brain awaits it, and both the CLI (awaitBind) and the desktop surface the message. BrainStatus carries the outcome instead of it dying in a console:

+ receiverError: string | null;  // brain is up, but nothing reaches the lasers
+ lastError: string | null;      // why the last start attempt failed

Receiver failure stays non-fatal (the show still runs console-only) but is now reported as its own amber warning rather than looking healthy.

"Use project" moves every panel. Project-scoped hooks refetch on their project argument, but two things didn't follow: a project pinned by Config on a row kept overriding the active one, and the embedded artist UI is served on the same origin whichever project runs, so its WebContentsView never reloaded. projects:use now drops the pin and restarts a live brain onto the new project; brain:start invalidates the laser view. Saving config does the same, since the brain resolves its config once at startup. did-fail-load also clears loadedUrl so a failed first load isn't remembered as loaded.

OSC loopback. OSC is UDP, so a wrong host fails with no error at all — and localhost is the trap: it can resolve to IPv6 ::1 while BEYOND/FB4 listens on IPv4. normalizeOscHost rewrites every loopback alias to 127.0.0.1 wherever a host is stored (CLI wizard, flags, desktop), the BEYOND wizard defaults to it, and the Output route gets a This machine button plus a ? explaining host vs. LAN IP.

Receiver, socket and OSC protocols are untouched.

Link to Devin session: https://app.devin.ai/sessions/972698f89f494b86828010666a002b8f
Requested by: @pyramation

…n use

The artist UI's dot was a bare boolean: no reason, no retry. It now
diagnoses a drop against /api/config + /api/me (server unreachable /
session expired / socket rejected / restarting) and reconnects with
backoff.

Startup failures were swallowed. The server handle exposes a ready
promise so a port clash surfaces as an actionable message instead of a
show that reports running with nothing listening, the brain records the
failure in its status, and the Show route prints it — plus a separate
receiver-failed-but-brain-up warning.

Switching the project in use now takes every panel with it: the pinned
config project is dropped and a live brain restarts onto the new project,
reloading the embedded artist UI (same origin, so it kept the old layout).

OSC hosts normalize loopback aliases to 127.0.0.1 — localhost can resolve
to IPv6 while BEYOND listens on IPv4, and UDP drops the packets silently.
The Output route gains a This machine button and a host help toggle.
@pyramation pyramation self-assigned this Aug 9, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 9a907d5 into main Aug 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant