feat(cloud): guided per-platform connect + onboarding on the cloud dashboard (0214) - #247
Merged
Merged
Conversation
…on the dashboard Implements the core of exploration 0214 — make the cloud dashboard teach a just-subscribed user how to connect their apps, not just list management cards. - connectCard() → per-platform tabs (Web / Desktop / Mobile), each with tailored steps. Desktop names the exact "Settings → Network → Signaling server" path and shows the hub URL in a copyable field (the real gap: the desktop app has no "Connect xNet Cloud hub" button today). Progressive enhancement: tab bar is hidden until JS, panels render unhidden + headed so no-JS shows all three stacked. - getting-started checklist derived from tenant state (plan / hub running / connect a device); self-completes and vanishes once a device binds. A client-set xnet_gs_hidden cookie (read server-side) lets users dismiss it early — no schema change. - copyField() + dashScript(): a new always-run vanilla-JS block (liveScript() early-returns without live tiles, which is exactly the unconnected state) handling tab switching, copy-to-clipboard (clipboard API → execCommand → select fallback), and the checklist Hide. - help footer (Connect guide / FAQ / Status / Self-host / About Cloud) on every state + a contextual help link by the connect card; marketingUrl threaded into DashboardView and derived into the link set. - hub card Endpoint is now copyable too. Checks off the implementation items in 0214. Tests + the cloud-connect doc follow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 new dashboard render tests (18 total): per-platform tabs render with all panels, the no-JS fallback (tab bar hidden, panels visible + headed), the exact desktop Settings copy + copyable hub URL, distinct copy-field ids (no duplicate), hostile-URL escaping, the state-derived checklist (shows pending / self-completes / cookie-dismissed / absent when tenantless), and help-footer link derivation (default, misconfigured `/`, and custom marketing origin). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A user-facing guide the dashboard links to: the two-identity (billing vs passkey) model, per-platform connect steps (Web / Desktop / Mobile via Tabs + Steps), the device-authorization code flow explained with a mermaid sequence diagram, and troubleshooting. Linked from the docs index guide grid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s, changelog - Add docs/guides/cloud-connect to site/src/sidebar.mjs (the site build fails the llms-full.txt step for any page missing from the sidebar — caught by a local `pnpm --filter site build`, since PR CI does not build the site). - Regenerate site/public/llms-full.txt to include the new guide. - Add the changelog fragment for the guided-connect dashboard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A 22-agent adversarial review (7 dimensions, each finding verified) surfaced these,
all now fixed:
- [hidden] override (real PE bug): `.tabs { display: flex }` and
`button { display: inline-block }` beat the UA `[hidden]` rule, so the no-JS tab
bar and the Hide button rendered visible-but-inert — defeating the very fallback
the tests claim. Added `[hidden] { display: none !important }`. Verified in-browser:
a hidden `.tabs` now computes `display: none`.
- Canceled/sleeping state (medium): connectCard() gated "Connected — pick up your
data" on `did` alone, so a canceled (hub torn down) or cold tenant saw it,
contradicting the adjacent hub/billing cards. Now returns '' when canceled and
shows an "asleep — opening the app wakes it" message when cold.
- Checklist resurrection: gettingStarted() re-appeared with a false "Hub running"
step for an already-onboarded tenant whose hub later slept/canceled. Now
early-returns once a device is bound (monotonic) or the sub is canceled.
- Accessibility: completed the WAI-ARIA tab pattern (id/aria-controls on tabs,
role=tabpanel/aria-labelledby/tabindex on panels) and added keyboard nav
(Arrow/Home/End + roving tabindex). Verified in-browser.
- Dead `#faq` anchor: added `id="faq"` to the pricing FAQ section so the dashboard +
guide links resolve.
Tests: strengthened checklist done-derivation assertions; added cold-tier,
canceled-tenant, sleeping-connected, footer-on-connected, trailing-slash, and ARIA
coverage. 23 tests, all green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tests/lint/typecheck, the rendered-state screenshots, the no-JS + [hidden] fix verification, copy wiring, and help-link resolution are done. Staging redeploy + manual dial-in remain (they happen on merge / are operator steps). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
|
Preview removed for PR #247. |
crs48
added a commit
that referenced
this pull request
Jun 24, 2026
…249) Fast-follow to the guided-connect cloud dashboard ([#247](#247)) — item **D2** from [exploration 0214](docs/explorations/0214_%5B_%5D_INTUITIVE_CLOUD_DASHBOARD_GUIDED_CONNECT_AND_ONBOARDING.md). Turns the dashboard's Desktop "paste this URL into Settings" flow into one click: **"Open in desktop app" → `xnet://connect?hub=<wss>` → the Electron app pre-fills + connects after you confirm.** ## Security (the crux) An `xnet://connect` link is an open redirect into a native app, so it's gated **twice**: 1. **Hard validation in the main process** — new pure, unit-tested [`apps/electron/src/main/deep-link.ts`](apps/electron/src/main/deep-link.ts): `parseConnectDeepLink` + `isAllowedHubUrl` require **`wss://`**, an **xNet-host allowlist** (`xnet.fyi`/`xnet.app` + subdomains; env-overridable via `XNET_ALLOWED_HUB_HOSTS`, mirroring the existing share-endpoint policy), **reject embedded credentials**, and bound the length. Rejects lookalikes (`evilxnet.fyi`, `xnet.fyi.evil.com`), `ws://`, `http(s)://`. 2. **Explicit user confirmation in the renderer** — new [`ConnectHubDialog`](apps/electron/src/renderer/components/ConnectHubDialog.tsx) shows the exact hub and **never auto-connects**; Cancel leaves the current hub untouched. ## What's in it - **Main** ([index.ts](apps/electron/src/main/index.ts)): `handleDeepLink` routes a validated payload to the renderer over `xnet:cloud-connect`, with cold-launch `pending` delivery on `did-finish-load`. Preload exposes `onCloudConnect`. - **Renderer**: on confirm, persist the hub via the new [`hub-url.ts`](apps/electron/src/renderer/lib/hub-url.ts) (mirrors the web `setPersistedHubUrl`; also read on boot in [`ipc-sync-manager.ts`](apps/electron/src/renderer/lib/ipc-sync-manager.ts) and in **Settings → Network**), then apply it live via `configureShareSession` (re-points sync, no restart). - **Dashboard** ([dashboard.ts](apps/cloud/src/dashboard.ts)): replaces the "coming soon" note with the `xnet://connect` button (https→wss normalized, URL-encoded), keeping the copy-paste fallback. The button is emitted **only for an allowlisted xNet hub**, so staging (`*.run.app`) cleanly shows copy-paste instead of a dead button. Full in-app device-claim parity (the dual-proof binding) stays as the **D3** follow-up — passkey + code approval still finish in the dashboard. ## Tests - `deep-link.test.ts` (11), `hub-url.test.ts` (4), `dashboard.test.ts` (+3 → 26). Full cloud unit suite (205) green; eslint clean; prettier clean. - Changelog fragment added. Remaining manual check (can't be done headless): launch a packaged desktop build and click the button end-to-end. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements exploration 0214 — make the xNet Cloud dashboard teach a just-subscribed user how to connect their apps, instead of just listing management cards.
What changed
apps/cloud/src/dashboard.ts(server-rendered HTML, no bundle):hiddenuntil JS arms it; panels render unhidden + headed, so with JS off all three stack readably.xnet_gs_hiddencookie (read server-side) dismisses it early. No schema change.copyField()+dashScript()— a new always-run vanilla-JS block (liveScript()early-returns without live tiles, which is exactly the unconnected state) for tab switching, copy-to-clipboard (clipboard API →execCommand→ select fallback), keyboard tab nav, and the Hide.marketingUrlthreaded intoDashboardViewand derived into the link set. Hub card Endpoint is now copyable too.Docs: new
/docs/guides/cloud-connect(two-identity model, per-platform steps via Tabs+Steps, the device-code flow as a mermaid sequence, troubleshooting), linked from the docs index, registered insidebar.mjs, and folded intollms-full.txt. Addedid="faq"to the pricing FAQ so#faqlinks resolve.Quality
dashboard.test.ts), all green — tabs/panels, no-JS fallback, ARIA wiring, copyable hub URL + distinct ids, hostile-URL escaping, the state-derived checklist (pending/self-complete/cookie/tenantless/cold), canceled/sleeping states, and help-link derivation (default, misconfigured/, custom origin, trailing slash).[hidden]overridden by authordisplay→ no-JS fallback defeated), a medium state-logic issue (the "Connected" card showed for canceled/suspended tenants), checklist resurrection for slept/canceled tenants, an incomplete ARIA tab pattern + missing keyboard nav, and a dead#faqanchor. Two findings were correctly dismissed as non-issues.[hidden]fix (a hidden.tabsnow computesdisplay:none).pnpm --filter site build) — which caught a requiredsidebar.mjsregistration that PR CI does not (CI doesn't build the site).Deferred (tracked as fast-follow)
Per 0214:
xnet://connectdesktop deep link (D2) and full desktop cloud-claim parity (D3) are a separate Electron PR. The no-JS server-side checklist dismissal is left out (the dashboard is JS-dependent by design).Redeploy to staging
Merging touches
apps/cloud/**, which triggersdeploy-cloud.ymltocloud-staging.xnet.fyiwhenCLOUD_DEPLOY_ENABLEDis set (otherwise it's a one-command manual dispatch). Validation steps are in 0214's checklist.🤖 Generated with Claude Code