Skip to content

Commit 87d562d

Browse files
ammar-agentethanndickson
authored andcommitted
🤖 refactor: stack logo above version in title bar (#1634)
When macOS traffic lights are present (leftInset > 0), stack the mux logo above the version string to fit in constrained space. Otherwise keep the original side-by-side layout. **Changes:** - Conditional `flex-col` vs `items-center gap-2` based on `leftInset` - Smaller logo/badge/text sizes when stacked (`h-3` vs `h-4`, `text-[10px]` vs `text-xs`) - Restore `-translate-y-px` adjustment for side-by-side layout --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$0.29`_
1 parent fe1497f commit 87d562d

File tree

2 files changed

+64
-97
lines changed

2 files changed

+64
-97
lines changed

src/browser/components/TitleBar.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,13 @@ export function TitleBar() {
262262
)}
263263
style={leftInset > 0 ? { paddingLeft: leftInset } : undefined}
264264
>
265-
<div className={cn("mr-4 flex min-w-0 items-center gap-2", isDesktop && "titlebar-no-drag")}>
265+
<div
266+
className={cn(
267+
"mr-4 flex min-w-0",
268+
leftInset > 0 ? "flex-col" : "items-center gap-2",
269+
isDesktop && "titlebar-no-drag"
270+
)}
271+
>
266272
<Tooltip>
267273
<TooltipTrigger asChild>
268274
<div
@@ -273,9 +279,14 @@ export function TitleBar() {
273279
onClick={handleUpdateClick}
274280
onMouseEnter={handleIndicatorHover}
275281
>
276-
<div className="relative h-4 w-[35px] overflow-hidden">
282+
<div
283+
className={cn(
284+
"relative overflow-hidden",
285+
leftInset > 0 ? "h-3 w-[26px]" : "h-4 w-[35px]"
286+
)}
287+
>
277288
<MuxLogo
278-
className={cn("block h-full w-full", leftInset > 0 && "-translate-y-px")}
289+
className={cn("block h-full w-full", leftInset > 0 || "-translate-y-px")}
279290
/>
280291
{showUpdateShimmer && (
281292
<div
@@ -290,7 +301,12 @@ export function TitleBar() {
290301
/>
291302
)}
292303
</div>
293-
<div className="text-accent flex h-3.5 w-3.5 items-center justify-center">
304+
<div
305+
className={cn(
306+
"text-accent flex items-center justify-center",
307+
leftInset > 0 ? "h-3 w-3" : "h-3.5 w-3.5"
308+
)}
309+
>
294310
{updateBadgeIcon}
295311
</div>
296312
</div>
@@ -301,7 +317,12 @@ export function TitleBar() {
301317
</Tooltip>
302318
<Tooltip>
303319
<TooltipTrigger asChild>
304-
<div className="min-w-0 cursor-text truncate text-xs font-normal tracking-wider select-text">
320+
<div
321+
className={cn(
322+
"min-w-0 cursor-text truncate font-normal tracking-wider select-text",
323+
leftInset > 0 ? "text-[10px]" : "text-xs"
324+
)}
325+
>
305326
{gitDescribe ?? "(dev)"}
306327
</div>
307328
</TooltipTrigger>
Lines changed: 38 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,55 @@
11
/**
2-
* Workspace titlebar / header stories
2+
* Title bar stories - demonstrates title bar layout variants
33
*/
44

5+
import React from "react";
56
import { appMeta, AppWithMocks, type AppStory } from "./meta.js";
6-
import {
7-
NOW,
8-
createWorkspace,
9-
groupWorkspacesByProject,
10-
type GitStatusFixture,
11-
} from "./mockFactory";
12-
import { createGitStatusExecutor, expandProjects, selectWorkspace } from "./storyHelpers";
13-
import { GIT_STATUS_INDICATOR_MODE_KEY } from "@/common/constants/storage";
14-
import { within, userEvent, waitFor } from "@storybook/test";
15-
167
import { createMockORPCClient } from "@/browser/stories/mocks/orpc";
178

189
export default {
1910
...appMeta,
20-
title: "App/Titlebar",
11+
title: "App/TitleBar",
2112
};
2213

2314
/**
24-
* Git status tooltip in workspace header - verifies alignment is near the indicator.
25-
* The header uses tooltipPosition="bottom" which requires align="start" to stay anchored.
15+
* macOS desktop mode with traffic lights inset.
16+
* Logo is stacked above version to fit in constrained space.
2617
*/
27-
export const GitStatusTooltip: AppStory = {
18+
export const MacOSDesktop: AppStory = {
19+
decorators: [
20+
(Story) => {
21+
// Save and restore window.api to prevent leaking to other stories
22+
const originalApiRef = React.useRef(window.api);
23+
window.api = {
24+
platform: "darwin",
25+
versions: {
26+
node: "20.0.0",
27+
chrome: "120.0.0",
28+
electron: "28.0.0",
29+
},
30+
// This function's presence triggers isDesktopMode() → true
31+
getIsRosetta: () => Promise.resolve(false),
32+
};
33+
34+
// Cleanup on unmount
35+
React.useEffect(() => {
36+
const savedApi = originalApiRef.current;
37+
return () => {
38+
window.api = savedApi;
39+
};
40+
}, []);
41+
42+
return <Story />;
43+
},
44+
],
2845
render: () => (
2946
<AppWithMocks
30-
setup={() => {
31-
window.localStorage.setItem(GIT_STATUS_INDICATOR_MODE_KEY, JSON.stringify("line-delta"));
32-
33-
const workspaces = [
34-
createWorkspace({
35-
id: "ws-active",
36-
name: "feature/tooltip-test",
37-
projectName: "my-app",
38-
createdAt: new Date(NOW - 3600000).toISOString(),
39-
}),
40-
];
41-
42-
const gitStatus = new Map<string, GitStatusFixture>([
43-
[
44-
"ws-active",
45-
{
46-
ahead: 3,
47-
behind: 2,
48-
dirty: 5,
49-
outgoingAdditions: 150,
50-
outgoingDeletions: 30,
51-
headCommit: "WIP: Testing tooltip alignment",
52-
},
53-
],
54-
]);
55-
56-
// Select workspace so header is visible
57-
selectWorkspace(workspaces[0]);
58-
expandProjects(["/home/user/projects/my-app"]);
59-
60-
return createMockORPCClient({
61-
projects: groupWorkspacesByProject(workspaces),
62-
workspaces,
63-
executeBash: createGitStatusExecutor(gitStatus),
64-
});
65-
}}
47+
setup={() =>
48+
createMockORPCClient({
49+
projects: new Map(),
50+
workspaces: [],
51+
})
52+
}
6653
/>
6754
),
68-
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
69-
const canvas = within(canvasElement);
70-
71-
// Wait for the workspace header to render with git status
72-
await waitFor(
73-
() => {
74-
canvas.getByTestId("workspace-header");
75-
},
76-
{ timeout: 5000 }
77-
);
78-
79-
// Wait for git status to appear in the header specifically
80-
const header = canvas.getByTestId("workspace-header");
81-
await waitFor(
82-
() => {
83-
within(header).getByText("+150");
84-
},
85-
{ timeout: 5000 }
86-
);
87-
88-
// Hover over the git status indicator in the header (not the sidebar)
89-
const plusIndicator = within(header).getByText("+150");
90-
await userEvent.hover(plusIndicator);
91-
92-
// Wait for tooltip to appear with correct alignment (portaled with data-state="open")
93-
// The key fix: data-align="start" anchors tooltip near the indicator (not "center")
94-
await waitFor(
95-
() => {
96-
const tooltip = document.body.querySelector<HTMLElement>(
97-
'.bg-modal-bg[data-state="open"][data-align="start"]'
98-
);
99-
if (!tooltip) throw new Error("git status tooltip not visible with align=start");
100-
// Verify tooltip has expected structure
101-
within(tooltip).getByText("Divergence:");
102-
},
103-
{ timeout: 5000 }
104-
);
105-
106-
// Double-RAF to ensure layout is stable after async rendering
107-
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
108-
},
10955
};

0 commit comments

Comments
 (0)