Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ export function useDefaultContextMenuActions(): ContextMenuActionConfig<PaneView
ctx.actions.split("right", {
kind: "browser",
data: {
url: "http://localhost:3000",
mode: "preview",
url: "about:blank",
} as BrowserPaneData,
});
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import type { RendererContext, Tab } from "@superset/panes";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { GlobeIcon } from "lucide-react";
import { useCallback, useSyncExternalStore } from "react";
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Active tab visibility is no longer synchronized for browser panes. Since the webviews are floated at the document root and only hidden on detach, removing the tab-activity effect leaves inactive tab webviews visible, so they can paint over the active tab after a tab switch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/BrowserPane.tsx, line 4:

<comment>Active tab visibility is no longer synchronized for browser panes. Since the webviews are floated at the document root and only hidden on detach, removing the tab-activity effect leaves inactive tab webviews visible, so they can paint over the active tab after a tab switch.</comment>

<file context>
@@ -1,10 +1,9 @@
 import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
 import { GlobeIcon } from "lucide-react";
-import { useCallback, useEffect, useSyncExternalStore } from "react";
+import { useCallback, useSyncExternalStore } from "react";
 import { TbDeviceDesktop } from "react-icons/tb";
 import { electronTrpcClient } from "renderer/lib/trpc-client";
</file context>
Fix with Cubic

import { TbDeviceDesktop } from "react-icons/tb";
import { electronTrpcClient } from "renderer/lib/trpc-client";
import type { BrowserPaneData, PaneViewerData } from "../../../../types";

import { browserRuntimeRegistry } from "./browserRuntimeRegistry";
import { BrowserErrorOverlay } from "./components/BrowserErrorOverlay";
import { BrowserOverflowMenu } from "./components/BrowserOverflowMenu";
import { BrowserToolbar } from "./components/BrowserToolbar";
import { usePersistentWebview } from "./hooks/usePersistentWebview";

function getSingleBrowserPane(
tab: Tab<PaneViewerData>,
): { id: string; data: BrowserPaneData } | null {
const paneIds = Object.keys(tab.panes);
if (paneIds.length !== 1) return null;
const pane = tab.panes[paneIds[0]];
if (pane.kind !== "browser") return null;
return { id: pane.id, data: pane.data as BrowserPaneData };
}

export function getBrowserTabTitle(
tab: Tab<PaneViewerData>,
): string | undefined {
const browser = getSingleBrowserPane(tab);
if (!browser) return undefined;
if (browser.data.pageTitle) return browser.data.pageTitle;
if (browser.data.url && browser.data.url !== "about:blank") {
try {
return new URL(browser.data.url).hostname;
} catch {}
}
return undefined;
}

export function renderBrowserTabIcon(tab: Tab<PaneViewerData>) {
const browser = getSingleBrowserPane(tab);
if (!browser?.data.faviconUrl) return null;
return (
<img src={browser.data.faviconUrl} alt="" className="size-3.5 shrink-0" />
);
}

interface BrowserPaneProps {
ctx: RendererContext<PaneViewerData>;
}

function useBrowserState(paneId: string) {
return useSyncExternalStore(
useCallback(
(cb) => browserRuntimeRegistry.onStateChange(paneId, cb),
[paneId],
),
useCallback(() => browserRuntimeRegistry.getState(paneId), [paneId]),
);
}

export function BrowserPane({ ctx }: BrowserPaneProps) {
const paneId = ctx.pane.id;
const state = useBrowserState(paneId);
const { placeholderRef, reload } = usePersistentWebview({ paneId, ctx });

const isBlankPage = !state.currentUrl || state.currentUrl === "about:blank";

return (
<div className="relative flex flex-1 h-full">
<div ref={placeholderRef} className="w-full h-full" style={{ flex: 1 }} />
{state.error && !state.isLoading && (
<BrowserErrorOverlay error={state.error} onRetry={reload} />
)}
{isBlankPage && !state.isLoading && !state.error && (
<div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-background pointer-events-none">
<GlobeIcon className="size-10 text-muted-foreground/30" />
<div className="text-center">
<p className="text-sm font-medium text-muted-foreground/50">
Browser
</p>
<p className="mt-1 text-xs text-muted-foreground/30">
Enter a URL above, or instruct an agent to navigate
<br />
and use the browser
</p>
</div>
</div>
)}
</div>
);
}

interface BrowserPaneToolbarProps {
ctx: RendererContext<PaneViewerData>;
}

export function BrowserPaneToolbar({ ctx }: BrowserPaneToolbarProps) {
const paneId = ctx.pane.id;
const state = useBrowserState(paneId);

const handleOpenDevTools = useCallback(() => {
electronTrpcClient.browser.openDevTools.mutate({ paneId }).catch(() => {});
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Do not silently swallow openDevTools failures; log or otherwise surface the error so this failure path is observable.

(Based on your team's feedback about handling async errors and avoiding empty catch blocks.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/BrowserPane/BrowserPane.tsx, line 78:

<comment>Do not silently swallow `openDevTools` failures; log or otherwise surface the error so this failure path is observable.

(Based on your team's feedback about handling async errors and avoiding empty catch blocks.) </comment>

<file context>
@@ -0,0 +1,142 @@
+	const state = useBrowserState(paneId);
+
+	const handleOpenDevTools = useCallback(() => {
+		electronTrpcClient.browser.openDevTools.mutate({ paneId }).catch(() => {});
+	}, [paneId]);
+
</file context>
Fix with Cubic

}, [paneId]);

const handleGoBack = useCallback(() => {
browserRuntimeRegistry.goBack(paneId);
}, [paneId]);

const handleGoForward = useCallback(() => {
browserRuntimeRegistry.goForward(paneId);
}, [paneId]);

const handleReload = useCallback(() => {
browserRuntimeRegistry.reload(paneId);
}, [paneId]);

const handleNavigate = useCallback(
(url: string) => {
browserRuntimeRegistry.navigate(paneId, url);
},
[paneId],
);

const isBlankPage = !state.currentUrl || state.currentUrl === "about:blank";
const PaneHeaderActions = ctx.components.PaneHeaderActions;

return (
<div className="flex h-full w-full items-center justify-between min-w-0">
<BrowserToolbar
currentUrl={state.currentUrl}
pageTitle={state.pageTitle}
isLoading={state.isLoading}
canGoBack={state.canGoBack}
canGoForward={state.canGoForward}
onGoBack={handleGoBack}
onGoForward={handleGoForward}
onReload={handleReload}
onNavigate={handleNavigate}
/>
<div className="flex items-center shrink-0 pr-1">
<div className="mx-1.5 h-3.5 w-px bg-muted-foreground/60" />
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
onClick={handleOpenDevTools}
className="rounded p-0.5 text-muted-foreground/60 transition-colors hover:text-muted-foreground"
>
<TbDeviceDesktop className="size-3.5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom" showArrow={false}>
Open DevTools
</TooltipContent>
</Tooltip>
<BrowserOverflowMenu
paneId={paneId}
currentUrl={state.currentUrl}
hasPage={!isBlankPage}
/>
<div className="mx-1 h-3.5 w-px bg-muted-foreground/60" />
<PaneHeaderActions />
</div>
</div>
);
}
Loading
Loading