Skip to content
Open
33 changes: 18 additions & 15 deletions scripts/check-edit-route-parity.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,29 @@ const ROUTE_FILE_PATTERN = /^\+(page|layout|server)\.(svelte|ts)$/;

const REPO_ROOT = fileURLToPath(new URL("..", import.meta.url));

// The roots cover the two halves of an active edit session: the file-editor
// workspace and the dashboard-preview viz tree. Onboarding/deploy flows live
// outside these roots on both sides and are intentionally not parity-checked:
// - web-local: `(misc)/welcome`, `(misc)/deploy`
// - web-admin: `/-/edit/welcome`
// They render distinct UIs that don't share the editor's component composition,
// so divergence is expected.
const LOCAL_ROOTS = [
"web-local/src/routes/(application)/(workspace)",
"web-local/src/routes/(viz)",
];
const ADMIN_ROOT = "web-admin/src/routes/[organization]/[project]/-/edit";
const ADMIN_ROOTS = [
"web-admin/src/routes/[organization]/[project]/-/edit/(workspace)",
"web-admin/src/routes/[organization]/[project]/-/edit/(viz)",
];

// Logical paths that exist only in web-local by design. Keep a short reason
// comment on each entry so a future contributor can judge whether it's still
// load-bearing.
const LOCAL_ONLY_ALLOWLIST = [
// Citation URL routes
// TODO: ensure citations within the edit session get routed to the developer
// preview dashboards _within_ the edit session, not to the branch preview
// preview dashboards _within_ the edit session, not to the branch preview
// dashboards _outside_ the edit session.
"/-/ai/[conversationId]/message/[messageId]/+layout.ts",
"/-/ai/[conversationId]/message/[messageId]/-/open/+page.ts",
Expand All @@ -40,17 +50,10 @@ const LOCAL_ONLY_ALLOWLIST = [
"/dashboard/[name]/+page.ts",
];

const ADMIN_ONLY_ALLOWLIST = [
// We have a layout at the root on rill-dev, not under subpath like (application)/(workspace)/ or (viz)/
"/+layout.ts",

// Welcome is under (misc) in local. There will be a future PR that moves them to root.
"/welcome/+layout.svelte",
"/welcome/+layout.ts",
"/welcome/+page.svelte",
"/welcome/add-data/+page.svelte",
"/welcome/add-data/+page.ts",
];
// Empty today: every route under ADMIN_ROOTS has a web-local counterpart.
// Add an entry (with a reason comment) if cloud ever needs an editor route
// that has no local equivalent.
const ADMIN_ONLY_ALLOWLIST = [];

function walkRoutes(absRoot) {
const results = [];
Expand Down Expand Up @@ -101,7 +104,7 @@ function staleAllowlistEntries(allowlist, shouldExistIn) {

function main() {
const localRoutes = collect(LOCAL_ROOTS);
const adminRoutes = collect([ADMIN_ROOT]);
const adminRoutes = collect(ADMIN_ROOTS);

const missingInAdmin = diff(localRoutes, adminRoutes, LOCAL_ONLY_ALLOWLIST);
const missingInLocal = diff(adminRoutes, localRoutes, ADMIN_ONLY_ALLOWLIST);
Expand All @@ -126,7 +129,7 @@ function main() {
);
for (const p of missingInAdmin) console.error(` ${p}`);
console.error(
`\nFix: either mirror each route under ${ADMIN_ROOT}/, or add the path to LOCAL_ONLY_ALLOWLIST in scripts/check-edit-route-parity.js with a reason.`,
`\nFix: either mirror each route under web-admin/src/routes/[organization]/[project]/-/edit/(workspace)/ or (viz)/, or add the path to LOCAL_ONLY_ALLOWLIST in scripts/check-edit-route-parity.js with a reason.`,
);
}

Expand Down
6 changes: 5 additions & 1 deletion web-admin/src/features/embeds/EmbedHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import { featureFlags } from "@rilldata/web-common/features/feature-flags";
import LastRefreshedDate from "@rilldata/web-admin/features/dashboards/listing/LastRefreshedDate.svelte";
import ChatToggle from "@rilldata/web-common/features/chat/layouts/sidebar/ChatToggle.svelte";
import {
dashboardChatActions,
dashboardChatOpen,
} from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import type {
V1Resource,
V1ResourceName,
Expand Down Expand Up @@ -87,7 +91,7 @@
{#if showDashboardChat}
<div class="flex gap-x-4 items-center">
<LastRefreshedDate dashboard={activeResource?.name} />
<ChatToggle />
<ChatToggle open={dashboardChatOpen} actions={dashboardChatActions} />
</div>
{/if}
</div>
Expand Down
12 changes: 6 additions & 6 deletions web-admin/src/features/embeds/init-embed-public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { themeControl } from "@rilldata/web-common/features/themes/theme-control
import { getEmbedThemeStoreInstance } from "@rilldata/web-common/features/embeds/embed-theme";
import { EmbedStore } from "@rilldata/web-common/features/embeds/embed-store";
import {
chatOpen,
sidebarActions,
dashboardChatActions,
dashboardChatOpen,
} from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";

const STATE_CHANGE_THROTTLE_TIMEOUT = 200;
Expand Down Expand Up @@ -91,17 +91,17 @@ export default function initEmbedPublicAPI(): () => void {
});

registerRPCMethod("getAiPane", () => {
return { open: get(chatOpen) };
return { open: get(dashboardChatOpen) };
});

registerRPCMethod("setAiPane", (open: boolean) => {
if (typeof open !== "boolean") {
throw new Error("Expected open to be a boolean");
}
if (open) {
sidebarActions.openChat();
dashboardChatActions.openChat();
} else {
sidebarActions.closeChat();
dashboardChatActions.closeChat();
}
return true;
});
Expand Down Expand Up @@ -149,7 +149,7 @@ export default function initEmbedPublicAPI(): () => void {
AI_PANE_CHANGE_THROTTLE_TIMEOUT,
AI_PANE_CHANGE_THROTTLE_TIMEOUT,
);
const aiPaneUnsubscribe = chatOpen.subscribe((isOpen) => {
const aiPaneUnsubscribe = dashboardChatOpen.subscribe((isOpen) => {
aiPaneChangeThrottler.throttle(() => {
emitNotification("aiPaneChanged", {
open: isOpen,
Expand Down
12 changes: 12 additions & 0 deletions web-admin/src/features/navigation/nav-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ export function isEditPage({ route }: Pick<Page, "route">): boolean {
return !!route?.id?.startsWith("/[organization]/[project]/-/edit");
}

/**
* True when the page is the explore or canvas preview inside Cloud Rill
* Developer (`/-/edit/(viz)/{explore,canvas}/[name]`). `isMetricsExplorerPage`
* and `isCanvasDashboardPage` only match production routes, so this is the
* editor-side equivalent for surfaces that need to swap chat affordances.
*/
export function isEditDashboardPreviewPage({
route,
}: Pick<Page, "route">): boolean {
return !!route?.id?.startsWith("/[organization]/[project]/-/edit/(viz)/");
}

export function isProjectRequestAccessPage(page: Page): boolean {
return !!page.route.id?.startsWith(
"/[organization]/[project]/-/request-access",
Expand Down
27 changes: 21 additions & 6 deletions web-admin/src/features/projects/ProjectHeader.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
import type { PathOption } from "@rilldata/web-common/components/navigation/breadcrumbs/types";
import { useCanvas } from "@rilldata/web-common/features/canvas/selector";
import ChatToggle from "@rilldata/web-common/features/chat/layouts/sidebar/ChatToggle.svelte";
import {
dashboardChatActions,
dashboardChatOpen,
developerChatActions,
developerChatOpen,
} from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import GlobalDimensionSearch from "@rilldata/web-common/features/dashboards/dimension-search/GlobalDimensionSearch.svelte";
import StateManagersProvider from "@rilldata/web-common/features/dashboards/state-managers/StateManagersProvider.svelte";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
Expand All @@ -35,6 +41,7 @@
} from "../navigation/breadcrumb-selectors";
import {
isCanvasDashboardPage,
isEditDashboardPreviewPage,
isMetricsExplorerPage,
isProjectPage,
isPublicURLPage,
Expand Down Expand Up @@ -75,6 +82,8 @@
$: onCanvasDashboardPage = isCanvasDashboardPage($page);
$: onPublicURLPage = isPublicURLPage($page);

$: onEditDashboardPreview = isEditDashboardPreviewPage($page);

$: activeBranch = extractBranchFromPath($page.url.pathname);

$: loggedIn = !!$user.data?.user;
Expand Down Expand Up @@ -195,8 +204,11 @@

<div class="flex gap-x-2 items-center ml-auto">
{#if editContext}
{#if $developerChat}
<ChatToggle />
{#if $developerChat && !onEditDashboardPreview}
<ChatToggle open={developerChatOpen} actions={developerChatActions} />
{/if}
{#if $dashboardChat && onEditDashboardPreview}
<ChatToggle open={dashboardChatOpen} actions={dashboardChatActions} />
{/if}
<EditActions {organization} {project} {primaryBranch} />
{:else}
Expand Down Expand Up @@ -237,8 +249,11 @@
{#if $dimensionSearch && ready}
<GlobalDimensionSearch />
{/if}
{#if $dashboardChat && !onPublicURLPage}
<ChatToggle />
{#if $dashboardChat && !onPublicURLPage && !editContext}
<ChatToggle
open={dashboardChatOpen}
actions={dashboardChatActions}
/>
{/if}
{#if hasUserAccess}
<ExploreBookmarks
Expand All @@ -263,8 +278,8 @@
{#if $cloudEditing && projectPermissions.manageDev}
<EditButton {organization} {project} {activeBranch} {primaryBranch} />
{/if}
{#if $dashboardChat && !onPublicURLPage}
<ChatToggle />
{#if $dashboardChat && !onPublicURLPage && !editContext}
<ChatToggle open={dashboardChatOpen} actions={dashboardChatActions} />
{/if}
{#if hasUserAccess}
<CanvasBookmarks {organization} {project} canvasName={dashboard} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<script lang="ts">
import CanvasDashboardEmbed from "@rilldata/web-common/features/canvas/CanvasDashboardEmbed.svelte";
import CanvasProvider from "@rilldata/web-common/features/canvas/CanvasProvider.svelte";
import DashboardChat from "@rilldata/web-common/features/chat/DashboardChat.svelte";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors.ts";
import { useRuntimeClient } from "@rilldata/web-common/runtime-client/v2";
import type { PageData } from "./$types";

Expand All @@ -16,9 +18,12 @@
</svelte:head>

{#key client.instanceId}
<div class="h-full overflow-hidden">
<CanvasProvider {canvasName} instanceId={client.instanceId} showBanner>
<CanvasDashboardEmbed {canvasName} />
</CanvasProvider>
<div class="flex h-full overflow-hidden">
<div class="flex-1 overflow-hidden">
<CanvasProvider {canvasName} instanceId={client.instanceId} showBanner>
<CanvasDashboardEmbed {canvasName} />
</CanvasProvider>
</div>
<DashboardChat kind={ResourceKind.Canvas} />
</div>
{/key}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import DashboardChat from "@rilldata/web-common/features/chat/DashboardChat.svelte";
</script>

<div class="flex h-full overflow-hidden">
<div class="flex flex-1 overflow-hidden">
<div class="flex-1 overflow-hidden">
<slot />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import DeveloperChat from "@rilldata/web-common/features/chat/DeveloperChat.svelte";
import Navigation from "@rilldata/web-common/layout/navigation/Navigation.svelte";
</script>

<div class="flex flex-1 overflow-hidden">
<Navigation showFooterLinks={false} />
<section class="flex flex-1 overflow-hidden">
<div class="flex-1 overflow-hidden">
<slot />
</div>
<DeveloperChat />
</section>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@
import CtaLayoutContainer from "@rilldata/web-common/components/calls-to-action/CTALayoutContainer.svelte";
import CtaMessage from "@rilldata/web-common/components/calls-to-action/CTAMessage.svelte";
import ErrorPage from "@rilldata/web-common/components/ErrorPage.svelte";
import DeveloperChat from "@rilldata/web-common/features/chat/DeveloperChat.svelte";
import FileAndResourceWatcher from "@rilldata/web-common/features/entity-management/FileAndResourceWatcher.svelte";
import { themeControl } from "@rilldata/web-common/features/themes/theme-control";
import { editorRoutePrefix } from "@rilldata/web-common/layout/navigation/editor-routing";
import Navigation from "@rilldata/web-common/layout/navigation/Navigation.svelte";
import RuntimeProvider from "@rilldata/web-common/runtime-client/v2/RuntimeProvider.svelte";
import { useQueryClient } from "@tanstack/svelte-query";
import { onDestroy } from "svelte";
Expand Down Expand Up @@ -185,18 +183,10 @@
{onBeforeReconnect}
errorBody="Lost connection to the editing environment. Try ending the session and starting a new one."
>
<div class="flex flex-1 overflow-hidden">
{#if !inProjectWelcomePage}
<WelcomeRedirector />
<Navigation showFooterLinks={false} />
{/if}
<section class="flex flex-1 overflow-hidden">
<div class="flex-1 overflow-hidden">
<slot />
</div>
<DeveloperChat />
</section>
</div>
{#if !inProjectWelcomePage}
<WelcomeRedirector />
{/if}
<slot />
</FileAndResourceWatcher>
</RuntimeProvider>
{/key}
Expand Down
6 changes: 5 additions & 1 deletion web-common/src/features/canvas/CanvasPreviewCTAs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import { featureFlags } from "../feature-flags";
import { getFileHref } from "../../layout/navigation/editor-routing";
import ChatToggle from "@rilldata/web-common/features/chat/layouts/sidebar/ChatToggle.svelte";
import {
dashboardChatActions,
dashboardChatOpen,
} from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import ViewAsButton from "../dashboards/granular-access-policies/ViewAsButton.svelte";
import {
useDashboardPolicyCheck,
Expand Down Expand Up @@ -42,7 +46,7 @@
<ViewAsButton />
{/if}
{#if $dashboardChat}
<ChatToggle />
<ChatToggle open={dashboardChatOpen} actions={dashboardChatActions} />
{/if}
{#if !$readOnly}
<Button type="secondary" href={getFileHref(canvasFilePath)}>Edit</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getConversationManager } from "@rilldata/web-common/features/chat/core/conversation-manager";
import { ToolName } from "@rilldata/web-common/features/chat/core/types";
import { sidebarActions } from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import { developerChatActions } from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import { pollForFileCreation } from "@rilldata/web-common/features/entity-management/actions/actions.ts";
import { fileArtifacts } from "@rilldata/web-common/features/entity-management/file-artifacts";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
Expand Down Expand Up @@ -178,6 +178,7 @@ export async function createCanvasDashboardFromMetricsViewWithAgent(
const conversationManager = getConversationManager(client, {
conversationState: "browserStorage",
agent: ToolName.DEVELOPER_AGENT,
surface: "developer",
});

// Start a new conversation instead of continuing existing one
Expand All @@ -191,7 +192,7 @@ export async function createCanvasDashboardFromMetricsViewWithAgent(
generatingCanvas.set(true);

// 4. Start the chat with the generation prompt
sidebarActions.startChat(prompt);
developerChatActions.startChat(prompt);

// Wait for the stream to start async through the sidebar action.
await waitUntil(() => get(currentConversation.isStreaming));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Conversation } from "@rilldata/web-common/features/chat/core/conversation";
import { getConversationManager } from "@rilldata/web-common/features/chat/core/conversation-manager";
import { ToolName } from "@rilldata/web-common/features/chat/core/types";
import { sidebarActions } from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import { developerChatActions } from "@rilldata/web-common/features/chat/layouts/sidebar/sidebar-store";
import type { RuntimeClient } from "@rilldata/web-common/runtime-client/v2";
import { derived, get, type Readable } from "svelte/store";
import type { CustomChartComponent } from "./index";
Expand Down Expand Up @@ -39,6 +39,7 @@ export function sendToDevAgent(
const conversationManager = getConversationManager(client, {
conversationState: "browserStorage",
agent: ToolName.DEVELOPER_AGENT,
surface: "developer",
});

const existing = componentConversations.get(component.id);
Expand All @@ -51,7 +52,7 @@ export function sendToDevAgent(
}

const fullPrompt = buildPrompt(component, userPrompt);
sidebarActions.startChat(fullPrompt);
developerChatActions.startChat(fullPrompt);

// Track the conversation for this component so subsequent calls continue it
const conversation = get(conversationManager.getCurrentConversation());
Expand All @@ -69,6 +70,7 @@ export function getAgentStreamingStore(
const conversationManager = getConversationManager(client, {
conversationState: "browserStorage",
agent: ToolName.DEVELOPER_AGENT,
surface: "developer",
});

return derived(
Expand Down
Loading
Loading