Skip to content

feat: step 3 consolidate dashboard Preview into project navbar. Implement Dev Preview#9360

Draft
royendo wants to merge 79 commits intomainfrom
worktree-feat-preview-pr-3-consolidate-dashboard-preview-into-navbar
Draft

feat: step 3 consolidate dashboard Preview into project navbar. Implement Dev Preview#9360
royendo wants to merge 79 commits intomainfrom
worktree-feat-preview-pr-3-consolidate-dashboard-preview-into-navbar

Conversation

@royendo
Copy link
Copy Markdown
Contributor

@royendo royendo commented May 1, 2026

requires: #9358

  • Add Preview/Edit toggle to local ApplicationHeader between AI and Deploy; hidden when launched with --preview (locked).
  • Add Preview button to cloud editor ProjectHeader in editContext; routes to /org/project/@branch/{kind}/{name} for dashboard editors and /org/project/@Branch otherwise.
  • Remove the per-resource PreviewButton from ExploreWorkspace, CanvasWorkspace, and MetricsWorkspace.
  • Introduce previewModeLocked store sourced from data.previewMode.

INSERT DESCRIPTION HERE

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

royendo and others added 30 commits May 1, 2026 11:17
Hide sharing, bookmarks, alerts, reports, and settings entries when
viewing a non-primary branch. The branch view is intended as a preview
surface, so cloud-only features that operate on production (sharing
the project, creating alerts, managing settings, etc.) are gated off
to keep the experience focused on previewing the branch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the plan-based gate on Cluster Size so it always renders when slots
are reported, and hide the Branches sub-tab from the status page nav.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Show the Branches tab in production view; hide it only when viewing a
non-primary branch (`/@branch/...`), where the section is not relevant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Visiting `/-/alerts`, `/-/reports`, `/-/status`, or `/-/settings` on a
branch view now redirects to the branch home, since these sections are
hidden from the project nav on branch views. Also hides the Status tab
on branch views for consistency.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Clicking Edit on a branch view now navigates directly to the branch's
edit URL instead of opening the picker dialog. The dialog was the
right flow on production view (pick or create a dev branch), but on a
branch view the target is already known.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add Preview/Edit toggle to local `ApplicationHeader` between AI and Deploy; hidden when launched with `--preview` (locked).
- Add Preview button to cloud editor `ProjectHeader` in editContext; routes to /org/project/@branch/{kind}/{name} for dashboard editors and /org/project/@Branch otherwise.
- Remove the per-resource `PreviewButton` from `ExploreWorkspace`, `CanvasWorkspace`, and `MetricsWorkspace`.
- Introduce `previewModeLocked` store sourced from `data.previewMode`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Gate the Status tab on `readProdStatus` instead of `manageProject` so
editors (not just admins) can view deployment status. The
`read_prod_status` permission is already true for admin and editor
roles and false for viewers, matching the desired access.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Map `read_prod_status` / `read_dev_status` to `runtime.ReadInstance` so
non-managers (editors) can call `GetInstance({ sensitive: true })`.
Without this, the Status page's Tables sub-page hangs forever for
editors because it can't resolve the OLAP connector name.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bundles `worktree-feat-preview-pr-1-update-dev-navbar` (limits Reports,
Alerts, Settings, and the Branches sub-tab on branch views; jumps
straight into edit mode from branch views) and keeps the project Status
tab visible on branch views so editors can read deployment status from
any branch URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The earlier merge resolution accidentally kept `&& !isBranchView` on
the Status tab, contradicting the intent. Removing it so editors can
view deployment status from any branch URL.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Gate the navbar `BranchSelector` on `manageDev` instead of `readDev`.
Viewers can only see the production branch, so the pill is redundant
for them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Match the file under edit (`/files/dashboards/foo.yaml`) against the
project's explores and canvases, then route Preview to `/explore/foo`
or `/canvas/foo` for that dashboard. In preview mode, route Edit back
to the dashboard's file path. Falls back to `/dashboards` and `/` when
no dashboard is in context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The viz routes already render `ExplorePreviewCTAs` / `CanvasPreviewCTAs`,
which carry their own Edit affordance. Showing the navbar toggle here
would (a) display Preview on a page that already is the preview, and
(b) duplicate Edit when in preview mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When in Preview mode on non-viz routes (`/dashboards`, `/ai`, `/status`)
and `rill.yaml` defines a project-wide security policy, render the
ViewAsButton next to the Preview/Edit toggle. Per-dashboard ViewAs in
ExplorePreviewCTAs / CanvasPreviewCTAs continues to handle viz routes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Show the navbar `ViewAsButton` when any explore or canvas resource has
`securityRules` on its valid spec, in addition to the existing rill.yaml
project-wide check. Previously a project with only per-dashboard `security:`
blocks did not surface ViewAs on `/dashboards`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Skip rendering `ChatToggle` in the navbar when the user is in Preview
mode (covers `/dashboards`, `/ai`, `/status`). Viz routes already use
`ExplorePreviewCTAs` / `CanvasPreviewCTAs`, which carry their own chat
affordance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When the local app navigates between Developer and Preview, close the
AI chat sidebar and clear any selected mock user (resetting the dev
JWT). Without this the chat panel stayed open across the switch and a
stale "View as" persona kept rewriting requests, neither of which
makes sense in the new mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the standalone navbar `ViewAsButton` (local) and the avatar
`View as` submenu (cloud) with a unified split-button on the Preview
control:

- Left half: navigates to the dashboard preview (or back to the file
  in Edit mode). Switches its label to "Viewing as <email>" with an
  inline clear affordance when an impersonation is active.
- Right half: a `UserRoundSearch` dropdown trigger that opens the
  existing user picker — `ViewAsUserPopover` (cloud, gated on
  `manageProject`) or the mock-user list from `useMockUsers` (local,
  gated on any project security policy).

Drops the duplicate `View as` submenu from `AvatarButton`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PostCSS rejects `@apply truncate` on a selector that also matches
elements with `class="truncate"`. The inline class on the span already
applies the utility — drop the redundant rule.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…v on user pick

- Drop the always-on light-purple background and rounded corners on
  the right (View as) half. The tint now appears only on hover/active.
- After picking a user from the dropdown, navigate straight into the
  preview surface (cloud → `editPreviewHref`, local → `previewToggleHref`)
  so "Viewing as" lands the user where they can see the impersonated
  view immediately.
- Stop auto-clearing the local mock user when mode toggles; selecting a
  user triggers a Developer → Preview transition and the previous reset
  was undoing the impersonation it had just set. Chat panel still
  closes on mode change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
(Earlier commit `ab8580670` only landed a stray prettier reflow from
a shadow checkout; this commit lands the actual changes.)

- Split button: square corners + the right (View as) half is
  transparent by default; tint applies only on hover/active.
- Picking a user from the dropdown navigates straight into the
  preview surface — `editPreviewHref` (cloud) or `previewToggleHref`
  (local, when not already in Preview).
- Local: stop auto-clearing the mock user on mode toggle, since the
  Developer → Preview navigation now happens as part of the pick;
  clear stays available via the inline × on the "Viewing as" chip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Revert the earlier merge that swapped the toggle's left label to
"Viewing as <email>" when an impersonation was active. The split
button is back to a plain Preview/Edit + dropdown trigger; the
"Viewing as" pill renders separately next to it:

- Cloud (editor): existing `ViewAsUserChip` shown in `editContext` when
  `$viewAsUserStore` is set.
- Local: existing `ViewAsButton` shown only when `$selectedMockUserStore`
  is set, where its already-impersonating branch renders the chip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-introduces the redirect from the reverted commit, but in a way that
doesn't break in-flight client-side navigations like home-bookmark URL
restoration.

The previous attempt put the check on `[organization]/[project]/+layout.ts`,
which made `url` a dependency of that loader and caused it to re-run on
every URL change in the project — including the `goto(redirectUrl,
{ replaceState: true })` that `DashboardStateSync` fires to restore
bookmarked filters. The re-run interfered with the goto and the URL
ended up empty after bookmark restoration (regressed
`bookmarks.spec.ts:265 "Visiting home should restore home bookmark"`).

Section-scoped loaders only re-run on URL changes within the same
section, so they don't disturb bookmark restoration on
`/explore/<dashboard>`.
…-pr-1-update-dev-navbar

# Conflicts:
#	web-admin/src/routes/[organization]/[project]/-/settings/+layout.ts
#	web-admin/src/routes/[organization]/[project]/-/status/+layout.ts
- New `onPreviewClick` callback on `PreviewModeToggleButton` fires only
  on the Preview/Edit half. Picking a user from the dropdown still
  flips the mode but no longer triggers the reset.
- Local: replace the integrated split-button dropdown with the
  standalone `ViewAsButton` (always rendered when a project policy
  exists). Its default state shows the `👤 View as` pill; impersonating
  state shows the `Viewing as <email>` chip.
- Local: `onPreviewClick` resets the chat panel and clears any active
  mock-user impersonation.
- Cloud: `onPreviewClick` clears `viewAsUserStore` if set. Editor
  retains the split-button design.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In Developer mode the navbar shows the split Preview + UserRoundSearch
button (matching the Figma design and bringing back the inline mock-user
dropdown). In Preview mode it shows the standalone `ViewAsButton`
(default `👤 View as` pill / `Viewing as <email>` chip when active)
alongside a plain Edit button.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
royendo and others added 7 commits May 1, 2026 16:45
Extract `LocalFullPageChat` into
`web-common/src/features/preview-mode/AiChatPage.svelte` parameterized
by `basePath`. Both the local `/ai` layout and the cloud editor
`/-/edit/ai` page now thinly wrap it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…onents

- `StatusLayout.svelte`: side nav (Overview / Resources / Tables)
  parameterized by `basePath`. Both apps' status `+layout.svelte` thin-
  wrap it.
- `StatusOverviewPage.svelte`: project info card + resources +
  errors sections. Local passes `runtimeVersion` (via local service)
  and slots `TablesSection`; cloud uses defaults.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extract the resources filterable table + parse errors section into
`web-common/src/features/preview-mode/StatusResourcesPage.svelte`.
Both `/status/resources` and `/-/edit/status/resources` now thin-wrap
it. Drops the local-only `ParseErrorsSection` shim.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Move `LocalProjectTables.svelte` to
  `web-common/src/features/preview-mode/StatusTablesPage.svelte` and
  the supporting query helpers to
  `web-common/src/features/preview-mode/tables-selectors.ts`.
- Both `/status/tables` (local) and `/-/edit/status/tables` (cloud
  editor dev preview) now thin-wrap the shared page.
- Update local `TablesSection` to import the selector from web-common.

The cloud non-edit `/-/status/tables` keeps using web-admin's
`ProjectTables` for now — that surface is out of scope for this PR.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`PreviewModeNav` (web-common) replaces both web-local's
`PreviewModeNav` + `LocalProjectStatusIndicator` and web-admin's
`EditDevPreviewNav`. It takes a `basePath` prop so each consumer can
plug in its own URL prefix (empty for local, branch-aware
`/{org}/{project}/@{branch}/-/edit` for cloud).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Swap `CloudViewAsButton` (which lists real project users via
`createAdminServiceSearchProjectUsers`) for the shared web-common
`ViewAsButton` in the cloud editor's dev-preview chrome. Both surfaces
now drive impersonation through `updateDevJWT` →
`runtimeServiceIssueDevJWT`, sourcing test users from `mock_users` in
`rill.yaml` — the same security-policy testing flow available in
local.

The cloud non-edit prod surface keeps `CloudViewAsButton` (real
project users) since that flow is about previewing what an actual
user will see in production.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`runtimeServiceIssueDevJWT` rejects the cloud editor's JWT context
("invalid kid") — the dev-JWT issuer only trusts local-runtime keys.
Restore `CloudViewAsButton` (real project users via the admin API)
for the editor dev preview until the runtime side accepts editor
JWTs as authorized to mint dev tokens.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@royendo royendo changed the title feat: step 3 consolidate dashboard Preview into project navbar feat: step 3 consolidate dashboard Preview into project navbar. Implement Dev Preview May 1, 2026
royendo and others added 7 commits May 1, 2026 17:11
Add a reactive watcher in `ProjectHeader` that fires whenever the
chrome flips out of dev-preview back into the editor, regardless of
whether the user clicked the Edit toggle, used the back button, or
typed a URL. Same reset behavior as the existing `onPreviewClick`
hook, just covering the cases where the click handler doesn't run
because the link unmounts before propagation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror the parent project layout's View-as flow inside
`/-/edit/+layout.svelte`. When `viewAsUserStore` is set, fetch
deployment credentials with `userId: mockedUserId` (branch-scoped
when on a branch) and route the editor's `RuntimeProvider` through
those credentials instead of `GetProject`'s editor JWT.

Without this the editor kept using its own admin/dev JWT on
`/-/edit/dashboards|status|ai`, so security policies were never
enforced and impersonated users still saw every dashboard. The
`{#key}` on RuntimeProvider now also includes `mockedUserId` so the
runtime client re-mounts cleanly when impersonation toggles.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`RuntimeProvider.updateJwt` only invalidates the query cache when the
`authContext` flips. With both the editor JWT and the impersonated
JWT defaulting to "user", flipping impersonation re-mounted the
provider but TanStack returned the cached unfiltered listResources
data — so dashboards still appeared.

Pass `authContext="mock"` while a mock user is active so the JWT
swap actually busts the cache.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a reactive watcher in each app's project-scope layout that classifies
the current path into a "platform" and clears `viewAsUserStore` /
`selectedMockUserStore` (and closes the AI chat sidebar) whenever the
classification changes:

- Cloud: `cloud-editor` (`/-/edit/files|explore|canvas|...`),
  `cloud-dev-preview` (`/-/edit/dashboards|status|ai`), and `cloud-prod`
  (everything else under the project) are treated as separate platforms.
- Local: `local-editor` and `local-preview` flip with `previewModeStore`.

Each platform is a different runtime/permission context, so a stale
impersonation makes no sense across the boundary. This catches the
cases the per-button `onPreviewClick` doesn't (back button, address bar,
nav-link clicks, branch selector, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add a one-shot `skipNextPlatformReset` flag in
  `web-common/features/preview-mode/platform-reset.ts`. The local
  mock-user dropdown and the cloud editor's `ViewAsUserPopover` set it
  before navigating so the impersonation they just established
  survives the platform-change reset that fires immediately after.
- Allowlist the new cloud editor dev-preview routes
  (`/-/edit/dashboards|status|ai|status/{resources,tables}`) in
  `scripts/check-edit-route-parity.js`. Local serves the same
  surfaces from the top-level routes (`/dashboards|status|ai`) under
  preview mode rather than nesting them under the editor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
royendo and others added 14 commits May 1, 2026 18:02
Was only consumed by the workspace `PreviewButton` (removed earlier in
this PR). Leaving the dangling reactive declaration confuses
`@typescript-eslint/no-unused-vars` enough to crash CI on this file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`showProjectViewAs`, `anyDashboardHasPolicy`, `rillYamlPolicyCheck`,
and the `useRillYamlPolicyCheck` import are leftover from an earlier
iteration where the standalone View-as pill was gated on policy
existence. The current rendering uses `showStandaloneViewAs` (gated
only on non-viz, non-deploy routes), so the rest is dead code — and
the dangling reactive declarations were tripping the
`@typescript-eslint/no-unused-vars` rule into a crash on this file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror the local app's mode pill in the cloud editor: render `Developer`
on `/-/edit/files|explore|canvas|...` and `Preview` on the dev-preview
sub-routes (`/-/edit/dashboards|status|ai`). Same `<Tag>` component
local uses, gated on `editContext` so it only appears inside the editor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Render `MergePopover` on the cloud editor's dev-preview routes so
users can publish from the same surface where they verify the change.
Sits next to the Edit-back button and `CloudViewAsButton`.

The editor (non dev-preview) chrome already includes the merge action
via `EditActions`; this just surfaces it in the Preview chrome too.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extend the `inEditDevPreview` regex in the cloud edit layout and
`ProjectHeader` to also match `/-/edit/explore/[name]` and
`/-/edit/canvas/[name]`. The dashboard route still renders the actual
dashboard, but the editor's file tree and chat sidebar are now hidden
and the dev-preview chrome takes over, so a click from
`/-/edit/dashboards` lands in a full-screen preview.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Swap `PreviewModeNav` for the existing cloud `ProjectTabs` on the
dev-preview chrome. The Home/Dashboards/Status/AI tab set already
covers the surfaces the dev-preview cares about and avoids carrying
a second navigation paradigm in the editor.

Gate `ProjectTabs` on a new `showProjectTabs` flag that only matches
`/-/edit/{dashboards,status,ai}` so dashboard views (now inside
`inEditDevPreview` too) fill the full frame instead of stacking a
tab row above the dashboard.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two link target changes that keep the user inside the editor session:

- `editPreviewHref` fallback in `ProjectHeader` now lands on
  `${branch}/-/edit/dashboards` instead of the production project home,
  so clicking Preview from a non-explore/canvas editor route enters the
  dev-preview chrome.
- The dev-preview dashboards listing builds rows as
  `${branch}/-/edit/{explore,canvas}/{name}` so a click into a dashboard
  stays under `/-/edit/...` and renders inside the dev-preview chrome
  (full-screen preview without the in-workspace editor).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`EditActions` (Publish / Commit + Merge / Exit) was previously only on
the editor side. Render it on `inEditDevPreview` too so users can
publish or commit without bouncing back to the editor first — the
dev-preview chrome now stands on its own as a full editing context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The dev-preview dashboard routes use a `name` route param, not
`dashboard`, so the depth-2 breadcrumb segment came up empty in the
header. Fall back to `name` when on those routes so the trail reads
`org / project / branch / dashboard` like the production project view.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two routing fixes for the cloud editor's dev-preview chrome:

- `ProjectTabs` gains an `editMode` prop. When set, AI / Dashboards /
  Status / Reports / Alerts / Settings routes use `${branch}/-/edit/...`
  instead of `${branch}/-/...`, and the Home tab points to
  `${branch}/-/edit/dashboards` so all in-tab navigation stays inside
  the editor session. The cloud edit layout passes `editMode` whenever
  it renders the tabs.
- The cloud `ProjectHeader` builds a per-render copy of the project
  breadcrumb options that pins each option's `href` to
  `${branch}/-/edit/dashboards` while `inEditDevPreview` is true, so
  clicking the project crumb from `/-/edit/explore/[name]` lands back
  on the dev-preview listing instead of dropping the user out to the
  branch's production view.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In `editMode`, Home and Dashboards both pointed at
`${branch}/-/edit/dashboards`, which fed the keyed `{#each}` two
duplicate `tab.route` keys and tripped Svelte's `each_key_duplicate`
runtime error. There's no separate project-home page on the
dev-preview surface, so simply gate Home off when `editMode` is true.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When `inEditDevPreview` is true, set an explicit `href` on each
visualization breadcrumb option so picking a different dashboard from
the resource dropdown stays inside the dev-preview chrome
(`${branch}/-/edit/{explore|canvas}/{name}`) instead of falling back
to the production `${section}/${name}` path the breadcrumb linkMaker
would otherwise build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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