Skip to content

Commit 04cf136

Browse files
committed
Fix workspace title display names
1 parent 7ee92a2 commit 04cf136

7 files changed

Lines changed: 41 additions & 17 deletions

File tree

packages/web/src/features/command-palette/components/command-palette.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "../../../atoms/workspaces";
1818
import { useViewport } from "../../../hooks/use-viewport";
1919
import { useTranslation } from "../../../lib/i18n";
20+
import { formatWorkspaceLabel } from "../../notifications/format";
2021
import {
2122
bottomPanelHeightAtom,
2223
focusModeAtom,
@@ -382,7 +383,7 @@ function buildCommands(context: {
382383

383384
// Add workspace switch commands
384385
workspaces.forEach((ws) => {
385-
const workspaceLabel = ws.name || ws.path?.split("/").pop() || ws.path || ws.id;
386+
const workspaceLabel = formatWorkspaceLabel(ws) || ws.id;
386387

387388
commands.push({
388389
id: `switch-workspace-${ws.id}`,

packages/web/src/features/notifications/format.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ describe("formatWorkspaceLabel", () => {
2121
expect(formatWorkspaceLabel({ name: "My Project", path: "/tmp/foo" })).toBe("My Project");
2222
});
2323

24+
it("extracts the basename when the explicit name is a full path", () => {
25+
expect(
26+
formatWorkspaceLabel({
27+
name: "/home/spencer/workspace/coder-studio",
28+
path: "/home/spencer/workspace/coder-studio",
29+
})
30+
).toBe("coder-studio");
31+
});
32+
2433
it("falls back to the basename of the path", () => {
2534
expect(formatWorkspaceLabel({ path: "/home/spencer/workspace/coder-studio" })).toBe(
2635
"coder-studio"

packages/web/src/features/notifications/format.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ export function formatWorkspaceLabel(
2828
): string {
2929
if (!workspace) return "";
3030
const name = workspace.name?.trim();
31-
if (name) return name;
31+
if (name) return extractWorkspaceLeafName(name);
3232
const path = workspace.path?.trim();
3333
if (!path) return "";
34+
return extractWorkspaceLeafName(path);
35+
}
36+
37+
function extractWorkspaceLeafName(value: string): string {
3438
// Strip trailing slashes, then take last segment.
35-
const cleaned = path.replace(/[/\\]+$/, "");
39+
const cleaned = value.replace(/[/\\]+$/, "");
3640
const parts = cleaned.split(/[/\\]/);
3741
return parts[parts.length - 1] || cleaned;
3842
}

packages/web/src/features/topbar/components/tab.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ describe("WorkspaceTab", () => {
4343
routerMocks.navigate.mockReset();
4444
});
4545

46+
it("renders the folder basename when workspace name is a full path", () => {
47+
const workspace = {
48+
...createWorkspace("ws-2", "/home/spencer/workspace/coder-studio"),
49+
name: "/home/spencer/workspace/coder-studio",
50+
};
51+
const store = createStore();
52+
store.set(localeAtom, "en");
53+
54+
render(
55+
<Provider store={store}>
56+
<WorkspaceTab workspace={workspace} isActive={false} />
57+
</Provider>
58+
);
59+
60+
expect(screen.getByText("coder-studio")).toBeInTheDocument();
61+
expect(screen.queryByText("/home/spencer/workspace/coder-studio")).toBeNull();
62+
});
63+
4664
it("sets the active workspace without navigating when a tab is clicked", () => {
4765
const workspace = createWorkspace("ws-2", "/tmp/two");
4866
const store = createStore();

packages/web/src/features/topbar/components/tab.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { X } from "lucide-react";
1111
import type { FC } from "react";
1212
import { activeWorkspaceIdAtom } from "../../../atoms/workspaces";
1313
import { useTranslation } from "../../../lib/i18n";
14+
import { formatWorkspaceLabel } from "../../notifications/format";
1415
import { useWorkspaceCloseAction } from "../../workspace/actions/use-workspace-close-action";
1516

1617
interface WorkspaceTabProps {
@@ -31,11 +32,7 @@ export const WorkspaceTab: FC<WorkspaceTabProps> = ({ workspace, isActive }) =>
3132
const t = useTranslation();
3233
const setActiveWorkspace = useSetAtom(activeWorkspaceIdAtom);
3334
const closeWorkspace = useWorkspaceCloseAction();
34-
const displayName =
35-
workspace.name ||
36-
workspace.path?.split("/").filter(Boolean).pop() ||
37-
workspace.path ||
38-
workspace.id;
35+
const displayName = formatWorkspaceLabel(workspace) || workspace.id;
3936

4037
const handleClick = () => {
4138
setActiveWorkspace(workspace.id);

packages/web/src/features/workspace/views/mobile/mobile-topbar.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Workspace } from "@coder-studio/core";
22
import { Menu, Settings2 } from "lucide-react";
33
import { useTranslation } from "../../../../lib/i18n";
4+
import { formatWorkspaceLabel } from "../../../notifications/format";
45
import type { WorkspaceFullscreenController } from "../../actions/use-workspace-fullscreen";
56
import { WorkspaceFullscreenButton } from "../../components/workspace-fullscreen-button";
67

@@ -21,10 +22,7 @@ export function MobileTopBar({
2122
}: MobileTopBarProps) {
2223
const t = useTranslation();
2324
const workspaceLabel =
24-
activeWorkspace?.name ??
25-
activeWorkspace?.path?.split("/").filter(Boolean).pop() ??
26-
activeWorkspace?.path ??
27-
t("mobile.workspace_drawer.select_title");
25+
formatWorkspaceLabel(activeWorkspace) || t("mobile.workspace_drawer.select_title");
2826

2927
return (
3028
<header className="mobile-topbar">

packages/web/src/features/workspace/views/mobile/mobile-workspace-drawer.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { X } from "lucide-react";
44
import { useNavigate } from "react-router-dom";
55
import { activeWorkspaceIdAtom } from "../../../../atoms/workspaces";
66
import { useTranslation } from "../../../../lib/i18n";
7+
import { formatWorkspaceLabel } from "../../../notifications/format";
78
import { useWorkspaceCloseAction } from "../../actions/use-workspace-close-action";
89

910
interface MobileWorkspaceDrawerProps {
@@ -51,11 +52,7 @@ export function MobileWorkspaceDrawer({
5152

5253
<div className="mobile-workspace-drawer__list">
5354
{workspaces.map((workspace) => {
54-
const displayName =
55-
workspace.name ||
56-
workspace.path?.split("/").filter(Boolean).pop() ||
57-
workspace.path ||
58-
workspace.id;
55+
const displayName = formatWorkspaceLabel(workspace) || workspace.id;
5956

6057
return (
6158
<div

0 commit comments

Comments
 (0)