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 @@ -17,14 +17,14 @@ export function buildSetupPaneLayout(
const tabId = `tab-${crypto.randomUUID()}`;
return {
id: tabId,
titleOverride: t.label,
createdAt: Date.now(),
activePaneId: paneId,
layout: { type: "pane" as const, paneId },
panes: {
[paneId]: {
id: paneId,
kind: "terminal",
titleOverride: t.label,
data: { terminalId: t.id } as TerminalPaneData,
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { ContextMenuActionConfig, RendererContext } from "@superset/panes";
import {
type ContextMenuActionConfig,
type PaneRegistry,
type RendererContext,
resolveTabTitle,
} from "@superset/panes";
import { useMemo } from "react";
import {
LuColumns2,
Expand All @@ -18,7 +23,9 @@ import type {
TerminalPaneData,
} from "../../types";

export function useDefaultContextMenuActions(): ContextMenuActionConfig<PaneViewerData>[] {
export function useDefaultContextMenuActions(
paneRegistry: PaneRegistry<PaneViewerData>,
): ContextMenuActionConfig<PaneViewerData>[] {
const splitDownShortcut = useHotkeyDisplay("SPLIT_DOWN").text;
const splitRightShortcut = useHotkeyDisplay("SPLIT_RIGHT").text;
const splitWithChatShortcut = useHotkeyDisplay("SPLIT_WITH_CHAT").text;
Expand Down Expand Up @@ -115,7 +122,7 @@ export function useDefaultContextMenuActions(): ContextMenuActionConfig<PaneView
const items: ContextMenuActionConfig<PaneViewerData>[] =
otherTabs.map((tab) => ({
key: `move-to-${tab.id}`,
label: tab.titleOverride ?? tab.id,
label: resolveTabTitle(tab, tabs, paneRegistry),
onSelect: () => {
ctx.store
.getState()
Expand Down Expand Up @@ -154,6 +161,7 @@ export function useDefaultContextMenuActions(): ContextMenuActionConfig<PaneView
splitWithBrowserShortcut,
equalizePaneSplitsShortcut,
closePaneShortcut,
paneRegistry,
],
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,6 @@ function getSingleBrowserPane(
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export {
BrowserPane,
BrowserPaneToolbar,
getBrowserTabTitle,
renderBrowserTabIcon,
} from "./BrowserPane";
export { browserRuntimeRegistry } from "./browserRuntimeRegistry";
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ export function usePaneRegistry(
const name = getFileName(data.filePath);
return <FileIcon fileName={name} className="size-4" />;
},
getTitle: (ctx: RendererContext<PaneViewerData>) => {
getTitle: (pane) => getFileName((pane.data as FilePaneData).filePath),
renderTitle: (ctx: RendererContext<PaneViewerData>) => {
const data = ctx.pane.data as FilePaneData;
const name = getFileName(data.filePath);
return (
Expand Down Expand Up @@ -262,9 +263,15 @@ export function usePaneRegistry(
},
browser: {
getIcon: () => <Globe className="size-4" />,
getTitle: (ctx: RendererContext<PaneViewerData>) => {
const data = ctx.pane.data as BrowserPaneData;
return data.pageTitle || data.url;
getTitle: (pane) => {
const data = pane.data as BrowserPaneData;
if (data.pageTitle) return data.pageTitle;
if (data.url && data.url !== "about:blank") {
try {
return new URL(data.url).host;
} catch {}
}
return "Browser";
},
Comment on lines 264 to 275
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Browser pane header falls back to raw UUID when tab is unnavigated

browser.getTitle now returns undefined when the URL is "about:blank" and there's no pageTitle. Pane.tsx resolves the header title via:

pane.titleOverride ?? definition.getTitle?.(pane) ?? pane.id

When getTitle returns undefined and titleOverride is unset (which is the case for a freshly-opened browser tab), the pane header displays the raw pane UUID (e.g. "pane-abc123..."). Before this PR, browser.getTitle fell back to data.url (so at least "about:blank" appeared), and addBrowserTab set titleOverride: "Browser". Both safety nets are now gone.

Add a "Browser" fallback so unnavigated browser panes show a human-readable header title:

Suggested change
browser: {
getIcon: () => <Globe className="size-4" />,
getTitle: (ctx: RendererContext<PaneViewerData>) => {
const data = ctx.pane.data as BrowserPaneData;
return data.pageTitle || data.url;
getTitle: (pane) => {
const data = pane.data as BrowserPaneData;
if (data.pageTitle) return data.pageTitle;
if (data.url && data.url !== "about:blank") {
try {
return new URL(data.url).hostname;
} catch {}
}
return undefined;
},
getTitle: (pane) => {
const data = pane.data as BrowserPaneData;
if (data.pageTitle) return data.pageTitle;
if (data.url && data.url !== "about:blank") {
try {
return new URL(data.url).hostname;
} catch {}
}
return "Browser";
},

Comment thread
coderabbitai[bot] marked this conversation as resolved.
renderPane: (ctx: RendererContext<PaneViewerData>) => (
<BrowserPane ctx={ctx} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import { filterMatchingPresetsForProject } from "shared/preset-project-targeting
import type { StoreApi } from "zustand/vanilla";
import type { PaneViewerData, TerminalPaneData } from "../../types";

function makeTerminalPane(terminalId: string): CreatePaneInput<PaneViewerData> {
function makeTerminalPane(
terminalId: string,
titleOverride?: string,
): CreatePaneInput<PaneViewerData> {
return {
kind: "terminal",
titleOverride,
data: { terminalId } as TerminalPaneData,
};
}
Expand Down Expand Up @@ -84,8 +88,7 @@ export function useV2PresetExecution({
preset.commands[0] as string,
);
state.addTab({
titleOverride: preset.name || "Terminal",
panes: [makeTerminalPane(id)],
panes: [makeTerminalPane(id, preset.name || undefined)],
});
break;
}
Expand All @@ -94,16 +97,22 @@ export function useV2PresetExecution({
const ids = await Promise.all(
preset.commands.map((cmd) => createSessionWithCommand(cmd)),
);
const panes = ids.map((id) => makeTerminalPane(id));
const panes = ids.map((id) =>
makeTerminalPane(id, preset.name || undefined),
);
state.addTab({
titleOverride: preset.name || "Terminal",
panes:
panes.length > 0
? (panes as [
CreatePaneInput<PaneViewerData>,
...CreatePaneInput<PaneViewerData>[],
])
: [makeTerminalPane(crypto.randomUUID())],
: [
makeTerminalPane(
crypto.randomUUID(),
preset.name || undefined,
),
],
});
break;
}
Expand All @@ -114,8 +123,9 @@ export function useV2PresetExecution({
);
for (let i = 0; i < ids.length; i++) {
state.addTab({
titleOverride: preset.name || "Terminal",
panes: [makeTerminalPane(ids[i] as string)],
panes: [
makeTerminalPane(ids[i] as string, preset.name || undefined),
],
});
}
break;
Expand All @@ -127,14 +137,13 @@ export function useV2PresetExecution({
);
if (!activeTabId) {
state.addTab({
titleOverride: preset.name || "Terminal",
panes: [makeTerminalPane(id)],
panes: [makeTerminalPane(id, preset.name || undefined)],
});
break;
}
state.addPane({
tabId: activeTabId,
pane: makeTerminalPane(id),
pane: makeTerminalPane(id, preset.name || undefined),
});
break;
}
Expand All @@ -144,23 +153,29 @@ export function useV2PresetExecution({
preset.commands.map((cmd) => createSessionWithCommand(cmd)),
);
if (!activeTabId) {
const panes = ids.map((id) => makeTerminalPane(id));
const panes = ids.map((id) =>
makeTerminalPane(id, preset.name || undefined),
);
state.addTab({
titleOverride: preset.name || "Terminal",
panes:
panes.length > 0
? (panes as [
CreatePaneInput<PaneViewerData>,
...CreatePaneInput<PaneViewerData>[],
])
: [makeTerminalPane(crypto.randomUUID())],
: [
makeTerminalPane(
crypto.randomUUID(),
preset.name || undefined,
),
],
});
break;
}
for (const id of ids) {
state.addPane({
tabId: activeTabId,
pane: makeTerminalPane(id),
pane: makeTerminalPane(id, preset.name || undefined),
});
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export function useWorkspaceHotkeys({

useHotkey("NEW_GROUP", () => {
store.getState().addTab({
titleOverride: "Terminal",
panes: [
{
kind: "terminal",
Expand All @@ -51,14 +50,12 @@ export function useWorkspaceHotkeys({

useHotkey("NEW_CHAT", () => {
store.getState().addTab({
titleOverride: "Chat",
panes: [{ kind: "chat", data: { sessionId: null } as ChatPaneData }],
});
});

useHotkey("NEW_BROWSER", () => {
store.getState().addTab({
titleOverride: "Browser",
panes: [
{
kind: "browser",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ import { WorkspaceNotFoundState } from "./components/WorkspaceNotFoundState";
import { WorkspaceSidebar } from "./components/WorkspaceSidebar";
import { useDefaultContextMenuActions } from "./hooks/useDefaultContextMenuActions";
import { usePaneRegistry } from "./hooks/usePaneRegistry";
import {
getBrowserTabTitle,
renderBrowserTabIcon,
} from "./hooks/usePaneRegistry/components/BrowserPane";
import { renderBrowserTabIcon } from "./hooks/usePaneRegistry/components/BrowserPane";
import { useV2PresetExecution } from "./hooks/useV2PresetExecution";
import { useV2WorkspacePaneLayout } from "./hooks/useV2WorkspacePaneLayout";
import { useWorkspaceHotkeys } from "./hooks/useWorkspaceHotkeys";
Expand Down Expand Up @@ -93,7 +90,7 @@ function WorkspaceContent({
projectId,
});
const paneRegistry = usePaneRegistry(workspaceId);
const defaultContextMenuActions = useDefaultContextMenuActions();
const defaultContextMenuActions = useDefaultContextMenuActions(paneRegistry);

const selectedFilePath = useStore(store, (s) => {
const tab = s.tabs.find((t) => t.id === s.activeTabId);
Expand All @@ -108,7 +105,6 @@ function WorkspaceContent({
const state = store.getState();
if (openInNewTab) {
state.addTab({
titleOverride: filePath.split(/[/\\]/).pop(),
panes: [
{
kind: "file",
Expand Down Expand Up @@ -139,7 +135,6 @@ function WorkspaceContent({
hasChanges: false,
} as FilePaneData,
},
tabTitle: "Files",
});
},
[store],
Expand All @@ -148,9 +143,8 @@ function WorkspaceContent({
const openDiffPane = useCallback(
(filePath: string) => {
const state = store.getState();
const activeTab = state.tabs.find((t) => t.id === state.activeTabId);
if (activeTab) {
for (const pane of Object.values(activeTab.panes)) {
for (const tab of state.tabs) {
for (const pane of Object.values(tab.panes)) {
if (pane.kind !== "diff") continue;
const prev = pane.data as DiffPaneData;
state.setPaneData({
Expand All @@ -160,27 +154,28 @@ function WorkspaceContent({
path: filePath,
} as PaneViewerData,
});
state.setActivePane({ tabId: activeTab.id, paneId: pane.id });
state.setActiveTab(tab.id);
state.setActivePane({ tabId: tab.id, paneId: pane.id });
return;
}
}
state.openPane({
pane: {
kind: "diff",
data: {
path: filePath,
collapsedFiles: [],
} as DiffPaneData,
},
tabTitle: "Changes",
state.addTab({
panes: [
{
kind: "diff",
data: {
path: filePath,
collapsedFiles: [],
} as DiffPaneData,
},
],
});
},
[store],
);

const addTerminalTab = useCallback(() => {
store.getState().addTab({
titleOverride: "Terminal",
panes: [
{
kind: "terminal",
Expand All @@ -194,7 +189,6 @@ function WorkspaceContent({

const addChatTab = useCallback(() => {
store.getState().addTab({
titleOverride: "Chat",
panes: [
{
kind: "chat",
Expand All @@ -206,7 +200,6 @@ function WorkspaceContent({

const addBrowserTab = useCallback(() => {
store.getState().addTab({
titleOverride: "Browser",
panes: [
{
kind: "browser",
Expand Down Expand Up @@ -270,7 +263,6 @@ function WorkspaceContent({
registry={paneRegistry}
paneActions={defaultPaneActions}
contextMenuActions={defaultContextMenuActions}
getTabTitle={getBrowserTabTitle}
renderTabIcon={renderBrowserTabIcon}
renderBelowTabBar={() => (
<V2PresetsBar
Expand Down
2 changes: 0 additions & 2 deletions packages/panes/src/core/store/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,9 @@ describe("openPane", () => {

store.getState().openPane({
pane: tp("p1", "opened"),
tabTitle: "My Tab",
});

expect(store.getState().tabs).toHaveLength(1);
expect(store.getState().tabs[0]?.titleOverride).toBe("My Tab");
expect(store.getState().getActivePane()?.pane.data.label).toBe("opened");
});

Expand Down
3 changes: 1 addition & 2 deletions packages/panes/src/core/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export interface WorkspaceStore<TData> extends WorkspaceState<TData> {
newPane: CreatePaneInput<TData>;
}) => void;

openPane: (args: { pane: CreatePaneInput<TData>; tabTitle?: string }) => void;
openPane: (args: { pane: CreatePaneInput<TData> }) => void;

splitPane: (args: {
tabId: string;
Expand Down Expand Up @@ -406,7 +406,6 @@ export function createWorkspaceStore<TData>(
// No tab → create one
if (!tab || !activeTabId) {
get().addTab({
titleOverride: args.tabTitle,
panes: [args.pane],
});
return;
Expand Down
Loading
Loading