diff --git a/scripts/check-edit-route-parity.js b/scripts/check-edit-route-parity.js index edaf73bf3f9..4c02864975c 100644 --- a/scripts/check-edit-route-parity.js +++ b/scripts/check-edit-route-parity.js @@ -50,6 +50,18 @@ const ADMIN_ONLY_ALLOWLIST = [ "/welcome/+page.svelte", "/welcome/add-data/+page.svelte", "/welcome/add-data/+page.ts", + + // Dev-preview routes inside the cloud editor. Locally these surfaces live + // at the top level (`/dashboards`, `/status*`, `/ai`) since the local app + // toggles into Preview mode via the navbar instead of nesting it under an + // editor URL. Each editor page thinly wraps the same shared + // `web-common/features/preview-mode/*` component the local routes use. + "/dashboards/+page.svelte", + "/status/+layout.svelte", + "/status/+page.svelte", + "/status/resources/+page.svelte", + "/status/tables/+page.svelte", + "/ai/+page.svelte", ]; function walkRoutes(absRoot) { diff --git a/web-admin/src/features/authentication/AvatarButton.svelte b/web-admin/src/features/authentication/AvatarButton.svelte index 437aaf26727..edd5d583dcc 100644 --- a/web-admin/src/features/authentication/AvatarButton.svelte +++ b/web-admin/src/features/authentication/AvatarButton.svelte @@ -26,7 +26,6 @@ createAdminServiceGetCurrentUser, type V1ProjectPermissions, } from "../../client"; - import ViewAsUserPopover from "../view-as-user/ViewAsUserPopover.svelte"; import ThemeToggle from "@rilldata/web-common/features/themes/ThemeToggle.svelte"; export let projectPermissions: V1ProjectPermissions | undefined = undefined; @@ -35,7 +34,6 @@ let imgContainer: HTMLElement; let primaryMenuOpen = false; - let subMenuOpen = false; onMount(() => { const photoUrl = $user.data?.user?.photoUrl; @@ -90,29 +88,6 @@ {#if params.organization && params.project && projectPermissions} - {#if projectPermissions.manageProject} - - { - subMenuOpen = !subMenuOpen; - }} - > - View as - - - { - subMenuOpen = false; - primaryMenuOpen = false; - }} - /> - - - {/if} {#if params.dashboard} { const result = await call("/rilldata/openrtb"); expect(result).toBeUndefined(); }); + + it("does not redirect away from the deploying page", async () => { + // Conditions that would otherwise trigger a redirect: + // no prod deployment, an active editable dev deployment, no @branch in URL. + listDeploymentsMock.mockResolvedValue({ + deployments: [ + makeDeployment({ + environment: "dev", + editable: true, + branch: "edit-branch", + status: V1DeploymentStatus.DEPLOYMENT_STATUS_RUNNING, + }), + ], + }); + + const result = await call("/rilldata/openrtb/-/deploying"); + expect(result).toBeUndefined(); + }); }); }); diff --git a/web-admin/src/features/branches/deployment-utils.ts b/web-admin/src/features/branches/deployment-utils.ts index 4d680171316..6f4ef46344d 100644 --- a/web-admin/src/features/branches/deployment-utils.ts +++ b/web-admin/src/features/branches/deployment-utils.ts @@ -40,6 +40,10 @@ export async function maybeRedirectToEditableDeployment( project: string, url: URL, ) { + // The deploying page is a transitional progress screen for a prod deployment + // that is still provisioning. Do not redirect away from it. + if (url.pathname.endsWith("/-/deploying")) return; + const deploymentsResp = await queryClient.fetchQuery({ queryKey: getAdminServiceListDeploymentsQueryKey(organization, project, {}), queryFn: () => adminServiceListDeployments(organization, project, {}), diff --git a/web-admin/src/features/edit-session/EditActions.svelte b/web-admin/src/features/edit-session/EditActions.svelte index f1f21613035..92b4632da14 100644 --- a/web-admin/src/features/edit-session/EditActions.svelte +++ b/web-admin/src/features/edit-session/EditActions.svelte @@ -3,20 +3,52 @@ import { Button } from "@rilldata/web-common/components/button"; import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; - import { LogOut } from "lucide-svelte"; + import { extractErrorMessage } from "@rilldata/web-common/lib/errors"; + import { createRuntimeServiceGitStatus } from "@rilldata/web-common/runtime-client"; + import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; + import { GitBranch, LogOut } from "lucide-svelte"; import CommitPopover from "./CommitPopover.svelte"; import MergePopover from "./MergePopover.svelte"; + import PublishPopover from "./PublishPopover.svelte"; export let organization: string; export let project: string; export let branch: string; export let primaryBranch: string | undefined = undefined; + const client = useRuntimeClient(); + const gitStatusQuery = createRuntimeServiceGitStatus(client, {}); + $: closeHref = `/${organization}/${project}${branchPathPrefix(branch)}`; + $: managedGit = $gitStatusQuery.data?.managedGit; + $: gitStatusLoaded = $gitStatusQuery.data !== undefined; + // Show the parent-level error UI only when GitStatus has never loaded. + // After a successful load, TanStack keeps `data` populated through transient + // refetch errors, so the popovers stay mounted and the user keeps the toolbar. + $: gitStatusErrorMessage = + !gitStatusLoaded && $gitStatusQuery.isError + ? extractErrorMessage($gitStatusQuery.error) + : ""; - - +{#if gitStatusLoaded} + {#if managedGit} + + {:else} + + + {/if} +{:else if gitStatusErrorMessage} + + + + {gitStatusErrorMessage} + + +{/if} + {/snippet} + + +
+

+ Publish your changes to production and return to the project home. + Viewers will see updates as the project reconciles. +

+ + {#if errorMessage} +

{errorMessage}

+ {/if} +
+
+ + + + {#if alreadyOnPrimary} + Already on production + {:else if !primaryBranch || !currentBranch || !deploymentsLoaded} + Loading project... + {:else} + Publish your latest changes + {/if} + + +
diff --git a/web-admin/src/features/projects/ProjectHeader.svelte b/web-admin/src/features/projects/ProjectHeader.svelte index a04f968802a..740de149697 100644 --- a/web-admin/src/features/projects/ProjectHeader.svelte +++ b/web-admin/src/features/projects/ProjectHeader.svelte @@ -1,12 +1,19 @@ -
+
{#if onPublicURLPage} @@ -189,7 +280,7 @@ {activeBranch} - {:else if !onPublicURLPage && projectPermissions?.readDev} + {:else if !onPublicURLPage && projectPermissions?.manageDev} {/if} @@ -197,10 +288,49 @@ {/if}
- {#if editContext} + {#if editContext && inEditDevPreview} + {#if projectPermissions?.manageDev} + + {/if} + + + {:else if editContext} {#if $developerChat} {/if} + {#if $viewAsUserStore} + + {/if} + + + { + editorViewAsOpen = false; + // Preserve the impersonation across the editor → cloud-prod + // transition this navigation triggers. + skipNextPlatformReset(); + void goto(editPreviewHref); + }} + /> + + {:else} - {#if $viewAsUserStore} - + {#if projectPermissions?.manageDev} + {/if} {#if $cloudEditing && onProjectPage && projectPermissions.manageDev} diff --git a/web-admin/src/features/projects/ProjectPublishing.svelte b/web-admin/src/features/projects/ProjectPublishing.svelte new file mode 100644 index 00000000000..926924414ec --- /dev/null +++ b/web-admin/src/features/projects/ProjectPublishing.svelte @@ -0,0 +1,21 @@ + + + + + + + Publishing your project to production... + +

+ Setting up your production environment for the first time. This usually + takes about a minute. +

+ +
+
diff --git a/web-admin/src/features/projects/ProjectTabs.svelte b/web-admin/src/features/projects/ProjectTabs.svelte index 0c951097c68..e277fbfe36d 100644 --- a/web-admin/src/features/projects/ProjectTabs.svelte +++ b/web-admin/src/features/projects/ProjectTabs.svelte @@ -13,47 +13,57 @@ export let project: string; export let pathname: string; export let branchPrefix: string = ""; + /** When rendered inside the cloud editor's dev-preview chrome, route the + * tabs to `/-/edit/{section}` so navigation stays inside the editor + * session instead of dropping the user into the production project view. */ + export let editMode: boolean = false; const { chat, reports, alerts } = featureFlags; + $: base = `/${organization}/${project}${branchPrefix}`; + $: sectionPrefix = editMode ? `${base}/-/edit` : `${base}/-`; + $: tabs = [ { - route: `/${organization}/${project}${branchPrefix}`, + route: base, label: "Home", - hasPermission: true, + // In dev-preview chrome there's no separate project-home page — + // Home would collide with Dashboards (`/-/edit/dashboards`), so + // drop it. + hasPermission: !editMode, }, { - route: `/${organization}/${project}${branchPrefix}/-/ai`, + route: `${sectionPrefix}/ai`, label: "AI", hasPermission: $chat, }, { - route: `/${organization}/${project}${branchPrefix}/-/dashboards`, + route: `${sectionPrefix}/dashboards`, label: "Dashboards", hasPermission: true, }, { - route: `/${organization}/${project}${branchPrefix}/-/query`, + route: `${sectionPrefix}/query`, label: "Query", hasPermission: false, }, { - route: `/${organization}/${project}${branchPrefix}/-/reports`, + route: `${sectionPrefix}/reports`, label: "Reports", hasPermission: $reports, }, { - route: `/${organization}/${project}${branchPrefix}/-/alerts`, + route: `${sectionPrefix}/alerts`, label: "Alerts", hasPermission: $alerts, }, { - route: `/${organization}/${project}${branchPrefix}/-/status`, + route: `${sectionPrefix}/status`, label: "Status", hasPermission: projectPermissions.manageProject, }, { - route: `/${organization}/${project}${branchPrefix}/-/settings`, + route: `${sectionPrefix}/settings`, label: "Settings", hasPermission: projectPermissions.manageProject, }, diff --git a/web-admin/src/features/view-as-user/CloudViewAsButton.svelte b/web-admin/src/features/view-as-user/CloudViewAsButton.svelte new file mode 100644 index 00000000000..965d8e4d2f7 --- /dev/null +++ b/web-admin/src/features/view-as-user/CloudViewAsButton.svelte @@ -0,0 +1,59 @@ + + + + + {#snippet child({ props })} + {#if $viewAsUserStore} + + {:else} + + {/if} + {/snippet} + + + (open = false)} + /> + + diff --git a/web-admin/src/routes/[organization]/[project]/+layout.svelte b/web-admin/src/routes/[organization]/[project]/+layout.svelte index ec31aef10f0..4447770333e 100644 --- a/web-admin/src/routes/[organization]/[project]/+layout.svelte +++ b/web-admin/src/routes/[organization]/[project]/+layout.svelte @@ -47,6 +47,8 @@ import { cloudVersion } from "@rilldata/web-admin/features/telemetry/initCloudMetrics"; import { getThemedLogoUrl } from "@rilldata/web-admin/features/themes/organization-logo"; import { viewAsUserStore } from "@rilldata/web-admin/features/view-as-user/viewAsUserStore"; + import { sidebarActions } from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store"; + import { consumePlatformResetSkip } from "@rilldata/web-common/features/preview-mode/platform-reset"; import ErrorPage from "@rilldata/web-common/components/ErrorPage.svelte"; import { themeControl } from "@rilldata/web-common/features/themes/theme-control"; import { metricsService } from "@rilldata/web-common/metrics/initMetrics"; @@ -109,6 +111,32 @@ }; }); + // Reset View as + AI chat whenever the user moves between cloud "platforms": + // editor (`/-/edit/files|explore|canvas|...`), editor dev preview + // (`/-/edit/dashboards|status|ai`), and cloud prod (everything else). + // Each is a different runtime/permission context, so a stale impersonation + // makes no sense across the boundary. + function classifyCloudPlatform(pathname: string): string { + if (/\/-\/edit\/(dashboards|status|ai)(\/|$)/.test(pathname)) + return "cloud-dev-preview"; + if (/\/-\/edit(\/|$)/.test(pathname)) return "cloud-editor"; + return "cloud-prod"; + } + let prevCloudPlatform: string | null = null; + $effect(() => { + const platform = classifyCloudPlatform(page.url.pathname); + if (prevCloudPlatform !== null && prevCloudPlatform !== platform) { + if (consumePlatformResetSkip()) { + // The user just picked an impersonation from the View-as dropdown + // and the dropdown handler asked us to preserve it across this nav. + } else { + if (viewAsUserStore.get()) viewAsUserStore.set(null); + sidebarActions.closeChat(); + } + } + prevCloudPlatform = platform; + }); + // --- Queries (three auth strategies; cookie and token are mutually exclusive, // mock is an overlay that runs in parallel when View As is active) --- diff --git a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte index 1d80acbe29d..c6e3daf50b8 100644 --- a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.svelte @@ -1,13 +1,14 @@ - +{#if source === "publish"} + +{:else} + +{/if} diff --git a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.ts b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.ts index 2a2be6254f0..0d77b2e21b1 100644 --- a/web-admin/src/routes/[organization]/[project]/-/deploying/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/-/deploying/+page.ts @@ -2,8 +2,10 @@ import { DeployingDashboardUrlParam } from "@rilldata/web-common/features/projec export const load = ({ url: { searchParams } }) => { const deployingDashboard = searchParams.get(DeployingDashboardUrlParam); + const source = searchParams.get("source"); return { deployingDashboard, + source, }; }; diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/+layout.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/+layout.svelte index 858859ee42b..61ea2bcd960 100644 --- a/web-admin/src/routes/[organization]/[project]/-/edit/+layout.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/edit/+layout.svelte @@ -15,6 +15,7 @@ import EditSessionLoading from "@rilldata/web-admin/features/edit-session/EditSessionLoading.svelte"; import EditSessionTimeoutBanner from "@rilldata/web-admin/features/edit-session/EditSessionTimeoutBanner.svelte"; import ProjectHeader from "@rilldata/web-admin/features/projects/ProjectHeader.svelte"; + import ProjectTabs from "@rilldata/web-admin/features/projects/ProjectTabs.svelte"; import SlimProjectHeader from "@rilldata/web-admin/features/projects/SlimProjectHeader.svelte"; import { getThemedLogoUrl } from "@rilldata/web-admin/features/themes/organization-logo"; import CtaButton from "@rilldata/web-common/components/calls-to-action/CTAButton.svelte"; @@ -126,6 +127,19 @@ $: branchUrl = `/${organization}/${project}${branchPathPrefix(branch)}`; $: inProjectWelcomePage = isProjectWelcomePage($page); + // Dev-preview sub-routes render standalone, without the editor's file + // tree or chat sidebar. Includes the dashboard view routes + // (`/-/edit/{explore,canvas}/[name]`) so a click from the dashboards + // listing lands in a full-screen preview rather than the in-workspace + // edit view. + $: inEditDevPreview = !!$page.route.id?.match( + /\/-\/edit\/(dashboards|status|ai|explore|canvas)(\/|$)/, + ); + // ProjectTabs sits above the page content, but only on the project-level + // surfaces — hide it on dashboard views so the dashboard fills the frame. + $: showProjectTabs = !!$page.route.id?.match( + /\/-\/edit\/(dashboards|status|ai)(\/|$)/, + ); // Invalidating this query refetches a fresh JWT; `runtimeClient.getJwt()` // reads the updated value on the next call. Branch must be part of the @@ -196,8 +210,18 @@ {onBeforeReconnect} errorBody="Lost connection to the editing environment. Try ending the session and starting a new one." > + {#if showProjectTabs} + + {/if}
- {#if !inProjectWelcomePage} + {#if !inProjectWelcomePage && !inEditDevPreview} {/if} @@ -205,7 +229,9 @@
- + {#if !inEditDevPreview} + + {/if}
diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/ai/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/ai/+page.svelte new file mode 100644 index 00000000000..8b62815ce43 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/ai/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/dashboards/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/dashboards/+page.svelte new file mode 100644 index 00000000000..40531fed252 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/dashboards/+page.svelte @@ -0,0 +1,17 @@ + + + diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/status/+layout.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/status/+layout.svelte new file mode 100644 index 00000000000..da39225ed8a --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/status/+layout.svelte @@ -0,0 +1,14 @@ + + + + + diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/status/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/status/+page.svelte new file mode 100644 index 00000000000..3ef7493c75c --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/status/+page.svelte @@ -0,0 +1,24 @@ + + + diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/status/resources/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/status/resources/+page.svelte new file mode 100644 index 00000000000..0a8f772d6fb --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/status/resources/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/web-admin/src/routes/[organization]/[project]/-/edit/status/tables/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/edit/status/tables/+page.svelte new file mode 100644 index 00000000000..10c37562948 --- /dev/null +++ b/web-admin/src/routes/[organization]/[project]/-/edit/status/tables/+page.svelte @@ -0,0 +1,5 @@ + + + diff --git a/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte b/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte index 2a2f31fa5e0..9d8caf833c0 100644 --- a/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte +++ b/web-common/src/features/dashboards/granular-access-policies/ViewAsButton.svelte @@ -4,10 +4,9 @@ import { Chip } from "../../../components/chip"; import Add from "../../../components/icons/Add.svelte"; - import CaretDownIcon from "../../../components/icons/CaretDownIcon.svelte"; import Check from "../../../components/icons/Check.svelte"; - import EyeIcon from "../../../components/icons/EyeIcon.svelte"; import Spacer from "../../../components/icons/Spacer.svelte"; + import { UserRoundSearch } from "lucide-svelte"; import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2"; import { selectedMockUserStore } from "./stores"; import { useMockUsers } from "./useMockUsers"; @@ -28,12 +27,10 @@ {#if $selectedMockUserStore === null} {:else} - - {#if reconciling} - Dashboard preview available after reconciliation - {:else if disabled} - File errors must be resolved before previewing - {:else} - Preview dashboard - {/if} - - diff --git a/web-local/src/features/chat/LocalFullPageChat.svelte b/web-common/src/features/preview-mode/AiChatPage.svelte similarity index 86% rename from web-local/src/features/chat/LocalFullPageChat.svelte rename to web-common/src/features/preview-mode/AiChatPage.svelte index 75fd78a5738..6ce1f40e961 100644 --- a/web-local/src/features/chat/LocalFullPageChat.svelte +++ b/web-common/src/features/preview-mode/AiChatPage.svelte @@ -1,10 +1,8 @@
- { - chatInputComponent?.focusInput(); - }} - onNewConversationClick={() => { - chatInputComponent?.focusInput(); - }} + onConversationClick={() => chatInputComponent?.focusInput()} + onNewConversationClick={() => chatInputComponent?.focusInput()} /> -
@@ -90,7 +85,6 @@ width: 100%; background: var(--surface); } - .chat-main { flex: 1; display: flex; @@ -98,7 +92,6 @@ overflow: hidden; background: var(--surface); } - .chat-content { flex: 1; overflow: hidden; @@ -106,7 +99,6 @@ display: flex; flex-direction: column; } - .chat-messages-wrapper { flex: 1; overflow-y: auto; @@ -114,7 +106,6 @@ display: flex; flex-direction: column; } - .chat-input-section { flex-shrink: 0; background: var(--surface); @@ -122,7 +113,6 @@ display: flex; justify-content: center; } - .chat-input-wrapper { width: 100%; max-width: 48rem; @@ -137,7 +127,6 @@ max-width: none; padding: 0 1rem; } - .chat-input-section { padding: 1rem; } diff --git a/web-common/src/features/preview-mode/DashboardsPage.svelte b/web-common/src/features/preview-mode/DashboardsPage.svelte new file mode 100644 index 00000000000..3a8327afc28 --- /dev/null +++ b/web-common/src/features/preview-mode/DashboardsPage.svelte @@ -0,0 +1,43 @@ + + + + + + + + + + + + + diff --git a/web-common/src/features/preview-mode/PreviewModeNav.svelte b/web-common/src/features/preview-mode/PreviewModeNav.svelte new file mode 100644 index 00000000000..de3849972b6 --- /dev/null +++ b/web-common/src/features/preview-mode/PreviewModeNav.svelte @@ -0,0 +1,177 @@ + + + + + + + diff --git a/web-common/src/features/preview-mode/StatusLayout.svelte b/web-common/src/features/preview-mode/StatusLayout.svelte new file mode 100644 index 00000000000..b63c0ad99fc --- /dev/null +++ b/web-common/src/features/preview-mode/StatusLayout.svelte @@ -0,0 +1,56 @@ + + + + + + + diff --git a/web-common/src/features/preview-mode/StatusOverviewPage.svelte b/web-common/src/features/preview-mode/StatusOverviewPage.svelte new file mode 100644 index 00000000000..ff842f15790 --- /dev/null +++ b/web-common/src/features/preview-mode/StatusOverviewPage.svelte @@ -0,0 +1,166 @@ + + +
+
+

Project

+
+
+
+ Status + + {#if $projectParserQuery.isLoading || $resourcesQuery.isLoading} + + Loading + {:else if totalErrors > 0} + + {totalErrors} + {totalErrors === 1 ? "error" : "errors"} + {:else} + + Running + {/if} + +
+
+ Environment + {environmentLabel} +
+ {#if runtimeVersion} +
+ Runtime + {runtimeVersion} +
+ {/if} +
+ OLAP Engine + + {getOlapEngineLabel( + instance?.olapConnector + ? { name: instance.olapConnector } + : undefined, + )} + +
+
+ AI Connector + + {instance?.aiConnector && instance.aiConnector !== "admin" + ? formatConnectorName(instance.aiConnector) + : "Rill Managed"} + +
+
+
+ + handleClick()} + onChipClick={(kind) => handleClick([], [kind])} +/> + + + + handleClick(["error"])} + onParseErrorChipClick={() => handleClick(["error"])} + onKindChipClick={(kind) => handleClick(["error"], [kind])} +/> + + diff --git a/web-common/src/features/preview-mode/StatusResourcesPage.svelte b/web-common/src/features/preview-mode/StatusResourcesPage.svelte new file mode 100644 index 00000000000..bec47eb4474 --- /dev/null +++ b/web-common/src/features/preview-mode/StatusResourcesPage.svelte @@ -0,0 +1,123 @@ + + + $resourcesQuery.refetch()} + bind:selectedStatuses + bind:selectedTypes + bind:searchText +/> + + diff --git a/web-local/src/features/tables/LocalProjectTables.svelte b/web-common/src/features/preview-mode/StatusTablesPage.svelte similarity index 99% rename from web-local/src/features/tables/LocalProjectTables.svelte rename to web-common/src/features/preview-mode/StatusTablesPage.svelte index 53dd35f4865..20a0a9ec108 100644 --- a/web-local/src/features/tables/LocalProjectTables.svelte +++ b/web-common/src/features/preview-mode/StatusTablesPage.svelte @@ -17,7 +17,7 @@ import { writable } from "svelte/store"; import ModelsTable from "@rilldata/web-common/features/projects/status/tables/ModelsTable.svelte"; import ExternalTablesTable from "@rilldata/web-common/features/projects/status/tables/ExternalTablesTable.svelte"; - import { useInfiniteTablesList, useModelResources } from "./selectors"; + import { useInfiniteTablesList, useModelResources } from "./tables-selectors"; import { debounce } from "@rilldata/web-common/lib/create-debouncer"; import { filterTemporaryTables, diff --git a/web-common/src/features/preview-mode/platform-reset.ts b/web-common/src/features/preview-mode/platform-reset.ts new file mode 100644 index 00000000000..2e7e2dab625 --- /dev/null +++ b/web-common/src/features/preview-mode/platform-reset.ts @@ -0,0 +1,21 @@ +/** + * One-shot flag the View-as user pickers can set just before navigating, + * to tell the project-layout's platform-change watcher *not* to clear + * the impersonation it just established. + * + * Without this, picking a mock/real user from the split-button dropdown + * triggers a navigation across a platform boundary (editor → preview), + * the platform watcher fires, and the freshly-set impersonation gets + * wiped before the new page can render with it. + */ +let skip = false; + +export function skipNextPlatformReset(): void { + skip = true; +} + +export function consumePlatformResetSkip(): boolean { + if (!skip) return false; + skip = false; + return true; +} diff --git a/web-local/src/features/tables/selectors.ts b/web-common/src/features/preview-mode/tables-selectors.ts similarity index 100% rename from web-local/src/features/tables/selectors.ts rename to web-common/src/features/preview-mode/tables-selectors.ts diff --git a/web-common/src/features/workspaces/CanvasWorkspace.svelte b/web-common/src/features/workspaces/CanvasWorkspace.svelte index 4676d9926d0..cef1398adac 100644 --- a/web-common/src/features/workspaces/CanvasWorkspace.svelte +++ b/web-common/src/features/workspaces/CanvasWorkspace.svelte @@ -1,15 +1,11 @@ + +
+ onPreviewClick?.()}> + {mode} + + + {#if showViewAs} + + + {#snippet child({ props })} + + {/snippet} + + + + + + {/if} +
+ + diff --git a/web-common/src/layout/preview-mode-store.ts b/web-common/src/layout/preview-mode-store.ts index 7ecb4128f84..bd88afa687b 100644 --- a/web-common/src/layout/preview-mode-store.ts +++ b/web-common/src/layout/preview-mode-store.ts @@ -7,3 +7,10 @@ import { writable } from "svelte/store"; * closing the browser and returning always defaults to developer mode. */ export const previewModeStore = writable(false); + +/** + * True only when the runtime was started with `--preview`. The Preview/Edit + * toggle in the navbar is hidden when this is true: the user cannot exit + * preview mode without restarting the CLI. + */ +export const previewModeLocked = writable(false); diff --git a/web-local/src/features/dashboards/DashboardList.svelte b/web-local/src/features/dashboards/DashboardList.svelte deleted file mode 100644 index 0d0f5e40599..00000000000 --- a/web-local/src/features/dashboards/DashboardList.svelte +++ /dev/null @@ -1,57 +0,0 @@ - - - - - {#if $previewModeStore} - - - Create dashboards using your code editor, then return here to preview - them. - - - {:else} - - - - Deploy your project - - to share dashboards with your team - - - {/if} - - diff --git a/web-local/src/features/preview/PreviewModeNav.svelte b/web-local/src/features/preview/PreviewModeNav.svelte deleted file mode 100644 index 160b46376c9..00000000000 --- a/web-local/src/features/preview/PreviewModeNav.svelte +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - diff --git a/web-local/src/features/tables/TablesSection.svelte b/web-local/src/features/tables/TablesSection.svelte index 1671d07549e..e34d3faff39 100644 --- a/web-local/src/features/tables/TablesSection.svelte +++ b/web-local/src/features/tables/TablesSection.svelte @@ -1,7 +1,7 @@ - -{#if hasResourceErrorsLoading || projectParserLoading} - -{:else if hasResourceErrorsError || projectParserError} - -{:else if hasResourceErrors || hasParseErrors} - -{:else} - -{/if} diff --git a/web-local/src/routes/ai/+layout.svelte b/web-local/src/routes/ai/+layout.svelte index 5882ccf311a..8931a897cf7 100644 --- a/web-local/src/routes/ai/+layout.svelte +++ b/web-local/src/routes/ai/+layout.svelte @@ -1,9 +1,9 @@
- +
diff --git a/web-local/src/routes/dashboards/+page.svelte b/web-local/src/routes/dashboards/+page.svelte index a54fea6dfcb..39d0ffd00a0 100644 --- a/web-local/src/routes/dashboards/+page.svelte +++ b/web-local/src/routes/dashboards/+page.svelte @@ -1,8 +1,9 @@ - - - + + + Create dashboards using your code editor, then return here to preview them. + + diff --git a/web-local/src/routes/status/+layout.svelte b/web-local/src/routes/status/+layout.svelte index 8192ec6d79d..173885c2711 100644 --- a/web-local/src/routes/status/+layout.svelte +++ b/web-local/src/routes/status/+layout.svelte @@ -1,56 +1,7 @@ - -
- -
- -
-
-
- - + + + diff --git a/web-local/src/routes/status/+page.svelte b/web-local/src/routes/status/+page.svelte index 4f2f8bbb478..3a018bd3f6b 100644 --- a/web-local/src/routes/status/+page.svelte +++ b/web-local/src/routes/status/+page.svelte @@ -1,69 +1,13 @@ - -
-
-

Project

-
-
-
- Status - - {#if $projectParserQuery.isLoading || $resourcesQuery.isLoading} - - Loading - {:else if totalErrors > 0} - - {totalErrors} - {totalErrors === 1 ? "error" : "errors"} - {:else} - - Running - {/if} - -
-
- Environment - Development -
- {#if version} -
- Runtime - {version} -
- {/if} -
- OLAP Engine - - {getOlapEngineLabel( - instance?.olapConnector - ? { name: instance.olapConnector } - : undefined, - )} - -
-
- AI Connector - - {instance?.aiConnector && instance.aiConnector !== "admin" - ? formatConnectorName(instance.aiConnector) - : "Rill Managed"} - -
-
-
- - goToResources()} - onChipClick={(kind) => goToResources([], [kind])} -/> - - - - goToResources(["error"])} - onParseErrorChipClick={() => goToResources(["error"])} - onKindChipClick={(kind) => goToResources(["error"], [kind])} -/> - - + + + diff --git a/web-local/src/routes/status/ParseErrorsSection.svelte b/web-local/src/routes/status/ParseErrorsSection.svelte deleted file mode 100644 index ca3e071757b..00000000000 --- a/web-local/src/routes/status/ParseErrorsSection.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - - diff --git a/web-local/src/routes/status/resources/+page.svelte b/web-local/src/routes/status/resources/+page.svelte index 33be0bae31a..0a8f772d6fb 100644 --- a/web-local/src/routes/status/resources/+page.svelte +++ b/web-local/src/routes/status/resources/+page.svelte @@ -1,101 +1,5 @@ - $resourcesQuery.refetch()} - bind:selectedStatuses - bind:selectedTypes - bind:searchText -/> - - + diff --git a/web-local/src/routes/status/tables/+page.svelte b/web-local/src/routes/status/tables/+page.svelte index b1e46ecf905..10c37562948 100644 --- a/web-local/src/routes/status/tables/+page.svelte +++ b/web-local/src/routes/status/tables/+page.svelte @@ -1,5 +1,5 @@ - +