Skip to content

Commit a04f44b

Browse files
feat(frontend): redesign hub navigation, org creation, and project NUX (#5064)
## Summary - Refresh the hub dashboard chrome: new top bar, settings drawer, sidebar cleanup (drops `sidebar-toggle.tsx`), feedback / help buttons, restyled dropdowns/cards. - New "Create a new organization" page (`new-org-page.tsx`) with an animated gradient orb avatar + client-side image upload (resized to 256x256 JPEG and stored as a data URL on `org.logo`). The same `paletteForLetter` palette (orange / cream / blue / green / purple / pink / teal / yellow keyed off the org name) is used in the org switcher, user dropdown, and `cloud-organization-select` fallback avatars. - New `org-landing` page (`/orgs/$organization`) with project grid + inline members section. New project NUX (`actors-grid`, `namespaces-grid`, settings panels under `settings-pages/`) and a polished context-switcher. - `serverless-connection-check`: replace the broken `error.error.{message,details}` destructure with a tagged-union formatter over `RunnerConfigsServerlessMetadataError` (handles `invalidRequest`, `requestFailed`, `requestTimedOut`, `nonSuccessStatus`, `invalidResponseJson`, `invalidResponseSchema`, `invalidEnvoyProtocolVersion`). - `user-dropdown`: drop the invalid `side="left"` prop on `<DropdownMenuSubContent>` (Radix infers side from the trigger). - `vite.config.ts`: add `allowedHosts: ["local.staging.rivet.dev"]` so teammates can hit the dev tunnel without re-editing. ## Known limitations - **Org logo upload is client-only.** Image is stored as a base64 data URL in `org.logo` via `authClient.organization.create({ logo })`. Works end-to-end with no backend changes but bloats the org record for large pictures. Real object-storage upload requires a backend endpoint. - **Project-level cross-namespace builds.** The context-switcher's namespace popover still assumes the current-namespace engine token; listing actor names across other namespaces would need a new cloud-api endpoint. ## Test plan - [ ] `pnpm run check-types` passes (verified locally). - [ ] `pnpm test` passes (13 tests, verified locally). - [ ] Walk the chrome on `https://local.staging.rivet.dev` against the dev tunnel. - [ ] Create a new org (with + without avatar upload) and confirm the gradient + letter render in the user dropdown and switcher list. - [ ] Open an org with multiple projects and verify the org landing page, project tiles, and members section. - [ ] Trigger a serverless health-check failure and confirm the new error formatter renders a useful message for each variant.
1 parent c999f19 commit a04f44b

114 files changed

Lines changed: 8722 additions & 2401 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Hub frontend Foundry restyle
2+
3+
Mirror the visual language of `sandbox-agent-v2/dhaka/frontend/packages/website` (Foundry) across `cairo/frontend`. Phased so each chunk can land independently.
4+
5+
## Status: code-complete pending visual QA.
6+
7+
## Foundry traits mirrored
8+
- Dark mode only. Cool zinc-blacks (`#09090b`, `#0f0f11`, `#0c0c0e`) instead of cairo's warm-brown.
9+
- IBM Plex Sans (body + headings) + IBM Plex Mono.
10+
- Glass surfaces: `bg-white/[0.02] backdrop-blur-md border border-white/10`, hover border `white/20`.
11+
- White-on-black primary button: not adopted — kept Rivet's orange primary (brand).
12+
- `rounded-lg` (buttons / inputs) and `rounded-xl` (cards / modals).
13+
- Orange accent at `#ff4f00`/`#ff5500` (Rivet brand).
14+
- Utilities lifted: `.glass`, `.glass-strong`, `.glow-accent`, `.shine-top`, `.text-gradient-accent`.
15+
16+
## Non-goals
17+
- Keep FontAwesome (`@rivet-gg/icons`); did not swap to lucide.
18+
- Did not rewrite component file structure.
19+
- Did not touch workflow-diagram (XYFlow) or Shiki code-highlight colors.
20+
21+
## Files touched
22+
23+
### Phase 1 — Foundation
24+
- `frontend/packages/components/public/theme.css` — cool zinc HSL tokens, dropped light mode, IBM Plex `@import`.
25+
- `frontend/src/components/theme.css` — same updates (less likely to be loaded but kept in sync).
26+
- `frontend/src/components/tailwind-base.ts``fontFamily.sans` → IBM Plex Sans; added `fontFamily.mono`.
27+
- `frontend/src/index.css` — added `.glass`, `.glass-strong`, `.glow-accent`, `.shine-top`, `.text-gradient-accent` in `@layer components`.
28+
29+
### Phase 2 — Primitives
30+
- `frontend/src/components/ui/button.tsx` — base `rounded-lg`; rebuilt secondary/outline/ghost variants on `border-white/10` + hover `border-white/20` + `bg-white/[0.06]` hover bg.
31+
- `frontend/src/components/ui/input.tsx``bg-white/[0.02]`, `border-white/10`, hover lift.
32+
- `frontend/src/components/ui/textarea.tsx` — same.
33+
- `frontend/src/components/ui/select.tsx` — same on trigger.
34+
- `frontend/src/components/ui/card.tsx``rounded-xl border-white/10`.
35+
- `frontend/src/components/ui/dialog.tsx` — overlay `bg-background/70 backdrop-blur-md`; content `rounded-xl border-white/10 bg-card shadow-2xl`.
36+
37+
### Phase 3 — Chrome
38+
- `frontend/src/app/layout.tsx` — sidebar `border-r border-white/10`; `HeaderLink` active state `bg-white/[0.06]`.
39+
- `frontend/src/app/actor-builds-list.tsx` — active state `bg-white/[0.06]`.
40+
- `frontend/src/components/ui/popover.tsx` — glass surface, `rounded-lg`.
41+
- `frontend/src/components/ui/dropdown-menu.tsx` — glass surface on both Content + SubContent, `rounded-lg`.
42+
- `frontend/src/components/ui/tooltip.tsx` — glass surface.
43+
- `frontend/src/components/ui/sonner.tsx``theme="dark"`, glass toast surface, `rounded-lg`.
44+
- `frontend/src/components/ui/sheet.tsx` — overlay `bg-background/70 backdrop-blur-md`.
45+
46+
### Phase 4 — New flow surfaces
47+
- `frontend/src/app/forms/create-project-form.tsx` — type cards: glass + `border-white/10``border-primary glow-accent` when selected.
48+
- `frontend/src/app/actors-grid.tsx``GridCard` glass + hover border-shift; outer wrapper `rounded-xl border-white/10`; build icon container `bg-white/[0.06]`.
49+
- `frontend/src/app/agent-panel.tsx` — chat bubbles: agent `bg-white/[0.04]`, user `bg-primary/15 border-primary/20`; avatar `bg-white/[0.06]`; bubbles `rounded-lg`.
50+
- `frontend/src/app/namespace-agent-layout.tsx` — agent column wrapper `rounded-xl border-white/10`.
51+
52+
### Phase 5 — Polish
53+
- `frontend/src/components/ui/typography.tsx` — H1 `text-2xl lg:text-3xl tracking-tight`; H2 `text-2xl`; H3 `text-xl` (was H1 `text-xl lg:text-4xl`, H2 `text-3xl`, H3 `text-2xl`).
54+
- `frontend/src/components/mdx/index.tsx``Note` callout swapped from `text-gray-*` literals to tokens.
55+
- `frontend/src/components/actors/guard-connectable-inspector.tsx``text-gray-600``text-muted-foreground`.
56+
57+
## Visual QA checklist (Task #19)
58+
Walk these routes at `https://local.staging.rivet.dev`. Screenshot anything that regresses.
59+
60+
- `/login` — login form, Google button, Turnstile presence.
61+
- `/orgs/$org` — context switcher, project list.
62+
- `/orgs/$org/new` — new project creation form (the new flow I built).
63+
- `/orgs/$org/projects/$project` — namespace list redirect.
64+
- `/orgs/$org/projects/$project/ns/$namespace` — actors grid landing (the new flow).
65+
- `/orgs/$org/projects/$project/ns/$namespace?n=…` — list+inspector view, tabs (Config, State, Database, Logs, Queue, Workflow, Connections).
66+
- `/orgs/$org/projects/$project/ns/$namespace/settings` — settings page.
67+
- `/orgs/$org/projects/$project/ns/$namespace/billing` — billing.
68+
- Agent panel toggle (bottom-right pill) — collapse/expand, chat bubbles.
69+
- Modals reachable via header dropdowns: Feedback, CreateNamespace, CreateOrganization, OrgMembers.
70+
- Toasts (any mutation that triggers one).
71+
72+
## Approach
73+
- One PR per phase (or all-in-one if user prefers).
74+
- Verify in browser at `https://local.staging.rivet.dev` after each phase (vite + cloudflared tunnel running locally).

.claude/reference/feature-flags.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Frontend Feature Flags
2+
3+
The dashboard runs against several deployment flavors (full cloud, OSS self-host, enterprise/on-prem). A feature flag lets each flavor turn a capability on or off without forking the code. Flags are the mechanism that keeps one frontend build serving every flavor.
4+
5+
## Where flags live
6+
7+
All flags are defined in one place: [frontend/src/lib/features.ts](../../frontend/src/lib/features.ts). Consume them with:
8+
9+
```ts
10+
import { features } from "@/lib/features";
11+
12+
if (features.platform) {
13+
// cloud-platform-only UI
14+
}
15+
```
16+
17+
- Source of truth at runtime is the `VITE_FEATURE_FLAGS` env var: a comma-separated list of enabled flag names.
18+
- In dev (`import.meta.env.DEV`), a `localStorage` key `FEATURE_FLAGS` overrides the env var so a flavor can be simulated locally.
19+
- **Unset (`undefined`) means every flag is on** — that is the full cloud build. An empty/explicit list opts in only to the named flags.
20+
- Some flags imply others (e.g. `platform` requires `auth`; `acl` is implied by `platform`). Encode those dependencies in `features.ts`, not at each call site.
21+
22+
## Current flags
23+
24+
| Flag | Meaning |
25+
| --- | --- |
26+
| `auth` | Dashboard ships a login/register flow. NOT a proxy for "engine requires credentials." |
27+
| `platform` | Cloud platform stack: publishable-token endpoint, billing, projects, multi-tenancy. Implies `auth`. (`multitenancy` is a legacy alias accepted during rollover.) |
28+
| `acl` | Engine enforces token auth on the public endpoint. Implied by `platform`; set independently for enterprise. |
29+
| `billing` | Billing UI. |
30+
| `captcha` | Turnstile captcha on auth forms. Requires `auth`. |
31+
| `support` | Support/help affordances. |
32+
| `branding` | Rivet branding chrome. |
33+
| `datacenter` | Datacenter-related UI. |
34+
| `danger-zone` | Destructive settings actions (`features.dangerZone`). |
35+
36+
Deployment flavors map to flag sets roughly as: **cloud** = all on; **OSS** = `auth`/`platform`/`acl` off; **enterprise** = `acl` on, `auth`/`platform` off (engine enforces auth without a login UI). Do not treat `platform`/`auth` as "engine requires credentials" — that is `acl`.
37+
38+
## When to add a new flag
39+
40+
**A new pack of features must ship behind a feature flag whenever it is significant or not universally available across deployment flavors.** The goal is that every flavor can freely enable or disable it.
41+
42+
Add a flag when the feature:
43+
44+
- Is unavailable, restricted, or behaves differently on at least one deployment flavor (cloud / OSS / enterprise), OR
45+
- Introduces a substantial new surface (a whole panel, page, settings section, or subsystem) that a flavor may want to turn off.
46+
47+
Do **not** add a flag for:
48+
49+
- Small, universal changes (bug fixes, copy tweaks, layout polish, a single button that every flavor always shows).
50+
- Anything that is always on everywhere — that is just code.
51+
52+
Avoid flag sprawl: do not gate everything. One flag per meaningful capability, not per component.
53+
54+
**If you are unsure whether a feature needs a flag, confirm with the user before adding (or omitting) one.** When in doubt, ask rather than guessing — flags are hard to remove once flavors depend on them.
55+
56+
## Consistency rules
57+
58+
- Add the flag in `features.ts` with a one-line comment on what it gates and any implied dependencies; do not read `VITE_FEATURE_FLAGS` or `import.meta.env` directly elsewhere.
59+
- Name flags after the capability (`billing`, `support`), not the deployment (`enterprise-only`). Deployment flavor is composed from flags, not the other way around.
60+
- Encode flag-implies-flag relationships in `features.ts` so call sites stay simple booleans.
61+
- When a flag changes the set of flavors, update the deployment-flavor mapping above and any docs that describe what each flavor ships.

CLAUDE.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ docker-compose up -d
7979

8080
**Never push to `main` unless explicitly specified by the user.**
8181

82+
## Frontend Visual Changes
83+
84+
- For any frontend visual change, use the `agent-browser` skill to view the result in a browser instead of working blind. If it is not installed, prompt the user to install it.
85+
- The frontend dashboard dev server always runs at `http://localhost:43708/`. Check that it is already serving there before starting a new one.
86+
87+
## Frontend Forms
88+
89+
- Manage all user-submitted form state with `react-hook-form`. Do not hand-roll form state with `useState`-driven controlled inputs. Wrapping a controlled UI-library input (e.g. a shadcn `Select`) in RHF's `<Controller>` is still react-hook-form owning the state and is fine.
90+
91+
## Frontend Feature Flags
92+
93+
- The dashboard serves multiple deployment flavors (cloud / OSS / enterprise) from one build via flags in `frontend/src/lib/features.ts`; consume them through `import { features } from "@/lib/features"`, never by reading `VITE_FEATURE_FLAGS` directly.
94+
- Ship a new pack of features behind a feature flag whenever it is significant (a whole panel/page/subsystem) or not universally available across flavors, so each flavor can freely enable or disable it. Do not gate small universal changes, and do not make flags for everything.
95+
- If unsure whether a feature needs a flag, confirm the need with the user before adding or omitting one.
96+
- See [Feature flags](.claude/reference/feature-flags.md) for the flag list, flavor mapping, and consistency rules.
97+
8298
## Frontend Routing (TanStack Router)
8399

84100
### Route context vs loader data
@@ -327,6 +343,7 @@ Load these only when the task touches the topic.
327343
### Agent procedural (`.claude/reference/`)
328344

329345
- **[Testing](.claude/reference/testing.md)** — running RivetKit tests, Vitest filter gotchas, driver-test parity workflow, Rust test layout.
346+
- **[Feature flags](.claude/reference/feature-flags.md)** — frontend `features.*` system, deployment-flavor mapping, when to add a flag, consistency rules.
330347
- **[Build troubleshooting](.claude/reference/build-troubleshooting.md)** — DTS failures, NAPI rebuild, `JsActorConfig` field churn, tsup stale exports.
331348
- **[Docs sync](.claude/reference/docs-sync.md)** — full table of "when you change X, update docs Y". Consult before finishing a change.
332349
- **[Content frontmatter](.claude/reference/content-frontmatter.md)** — required frontmatter schemas for docs + blog/changelog.

frontend/packages/components/public/theme.css

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,56 @@
1+
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
2+
13
:root {
4+
/* Light tokens — kept as a fallback. App pins `<html class="dark">`
5+
in index.html so these only render if dark is ever removed. */
26
--background: 0 0% 100%;
3-
--foreground: 20 14.3% 4.1%;
7+
--foreground: 240 10% 4%;
48
--card: 0 0% 100%;
5-
--card-foreground: 20 14.3% 4.1%;
9+
--card-foreground: 240 10% 4%;
610
--popover: 0 0% 100%;
7-
--popover-foreground: 20 14.3% 4.1%;
8-
--primary: 24.6 95% 53.1%;
9-
--primary-foreground: 60 9.1% 97.8%;
10-
--secondary: 60 4.8% 95.9%;
11-
--secondary-foreground: 24 9.8% 10%;
12-
--muted: 60 4.8% 95.9%;
13-
--muted-foreground: 25 5.3% 44.7%;
14-
--muted-destructive: 0 72% 30%;
15-
--accent: 60 4.8% 95.9%;
16-
--accent-foreground: 24 9.8% 10%;
11+
--popover-foreground: 240 10% 4%;
12+
--primary: 18.5 100% 50%;
13+
--primary-foreground: 0 0% 100%;
14+
--secondary: 240 5% 96%;
15+
--secondary-foreground: 240 10% 4%;
16+
--muted: 240 5% 96%;
17+
--muted-foreground: 240 4% 46%;
18+
--accent: 240 5% 96%;
19+
--accent-foreground: 240 10% 4%;
1720
--destructive: 0 84.2% 60.2%;
18-
--destructive-foreground: 60 9.1% 97.8%;
19-
--border: 20 5.9% 90%;
20-
--input: 20 5.9% 90%;
21-
--ring: 24.6 95% 53.1%;
21+
--destructive-foreground: 0 0% 100%;
22+
--border: 240 6% 90%;
23+
--input: 240 6% 90%;
24+
--ring: 18.5 100% 50%;
2225
--radius: 0.5rem;
2326
}
2427
:root[class~="dark"] {
25-
--background: 20 14.3% 4.1%;
26-
--foreground: 60 9.1% 97.8%;
27-
--card: 0 9.09% 6.47%;
28-
--card-foreground: 60 9.1% 97.8%;
29-
--popover: 0 9.09% 6.47%;
30-
--popover-foreground: 60 9.1% 97.8%;
28+
/* Cool zinc neutrals — mirrors Foundry's #09090b / #0c0c0e / #0f0f11. */
29+
--background: 240 6% 4%;
30+
--foreground: 0 0% 100%;
31+
--card: 240 7% 5%;
32+
--card-foreground: 0 0% 100%;
33+
--popover: 240 7% 5%;
34+
--popover-foreground: 0 0% 100%;
3135
--primary: 18.5 100% 50%;
32-
--primary-foreground: 60 9.1% 97.8%;
33-
--secondary: 12 6.5% 15.1%;
34-
--secondary-foreground: 60 9.1% 97.8%;
35-
--muted: 34 10% 10%;
36-
--muted-foreground: 24 5.4% 63.9%;
37-
--accent: 12 6.5% 15.1%;
38-
--accent-foreground: 60 9.1% 97.8%;
36+
--primary-foreground: 0 0% 100%;
37+
--secondary: 240 5% 7%;
38+
--secondary-foreground: 0 0% 100%;
39+
--muted: 240 5% 9%;
40+
--muted-foreground: 240 5% 65%;
41+
--accent: 240 5% 9%;
42+
--accent-foreground: 0 0% 100%;
3943
--destructive: 0 72.2% 50.6%;
40-
--destructive-foreground: 60 9.1% 97.8%;
44+
--destructive-foreground: 0 0% 100%;
4145
--warning: 47.9 95.8% 53.1%;
42-
--border: 12 6.5% 15.1%;
43-
--input: 12 6.5% 15.1%;
46+
/* Visual equivalent of white/10 over the background. */
47+
--border: 240 5% 14%;
48+
--input: 240 5% 14%;
4449
--ring: 18.59deg 100% 50%;
45-
--background-main: 0 7.14% 5.49%;
46-
}
47-
48-
:root {
49-
--chart-1: 12 76% 61%;
50-
--chart-2: 173 58% 39%;
51-
--chart-3: 197 37% 24%;
52-
--chart-4: 43 74% 66%;
53-
--chart-5: 27 87% 67%;
50+
--background-main: 240 6% 4%;
5451
}
5552

53+
:root,
5654
.dark {
5755
--chart-1: 18.5 100% 50%;
5856
--chart-2: 160 60% 45%;

frontend/src/app/actor-builds-list.tsx

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)