Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
Prev Previous commit
Next Next commit
update reservation for traffic lights width on macos tahoe
  • Loading branch information
sawka committed Mar 13, 2026
commit ca8979b39655cfebe699d0769f93c1059d617541
6 changes: 6 additions & 0 deletions frontend/app/store/wshclientapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,12 @@ export class RpcApiType {
return client.wshRpcCall("listalleditableapps", null, opts);
}

// command "macosversion" [call]
MacOSVersionCommand(client: WshClient, opts?: RpcOpts): Promise<string> {
if (this.mockClient) return this.mockClient.mockWshRpcCall(client, "macosversion", null, opts);
return client.wshRpcCall("macosversion", null, opts);
}

// command "makedraftfromlocal" [call]
MakeDraftFromLocalCommand(client: WshClient, data: CommandMakeDraftFromLocalData, opts?: RpcOpts): Promise<CommandMakeDraftFromLocalRtnData> {
if (this.mockClient) return this.mockClient.mockWshRpcCall(client, "makedraftfromlocal", data, opts);
Expand Down
57 changes: 34 additions & 23 deletions frontend/app/tab/tabbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TabRpcClient } from "@/app/store/wshrpcutil";
import { useWaveEnv } from "@/app/waveenv/waveenv";
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
import { deleteLayoutModelForTab } from "@/layout/index";
import { isMacOSTahoeOrLater } from "@/util/platformutil";
import { fireAndForget } from "@/util/util";
import { useAtomValue } from "jotai";
import { OverlayScrollbars } from "overlayscrollbars";
Expand All @@ -20,6 +21,9 @@ import { WorkspaceSwitcher } from "./workspaceswitcher";

const TabDefaultWidth = 130;
const TabMinWidth = 100;
const MacOSTrafficLightsWidth = 74;
const MacOSTahoeTrafficLightsWidth = 80;

const OSOptions = {
overflow: {
x: "scroll",
Expand Down Expand Up @@ -636,10 +640,13 @@ const TabBar = memo(({ workspace, noTabs }: TabBarProps) => {
// Calculate window drag left width based on platform and state
let windowDragLeftWidth = 10;
if (env.isMacOS() && !isFullScreen) {
const trafficLightsWidth = isMacOSTahoeOrLater()
? MacOSTahoeTrafficLightsWidth
: MacOSTrafficLightsWidth;
if (zoomFactor > 0) {
windowDragLeftWidth = 74 / zoomFactor;
windowDragLeftWidth = trafficLightsWidth / zoomFactor;
} else {
windowDragLeftWidth = 74;
windowDragLeftWidth = trafficLightsWidth;
}
}

Expand Down Expand Up @@ -684,28 +691,32 @@ const TabBar = memo(({ workspace, noTabs }: TabBarProps) => {
<div
className="tabs-wrapper"
ref={tabsWrapperRef}
style={{ width: `${tabsWrapperWidth}px`, ...(noTabs ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : {}) }}
style={{
width: `${tabsWrapperWidth}px`,
...(noTabs ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : {}),
}}
Comment on lines +691 to +697
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.

⚠️ Potential issue | 🟡 Minor

Don't size the hidden strip by tab count.

When noTabs is true, this wrapper still expands to tabIds.length * tabWidthRef.current, so left-tab mode can end up with an empty horizontal scroller/drag region on large workspaces. Making the wrapper fill the available space avoids that blank strip.

Suggested change
                 <div
                     className="tabs-wrapper"
                     ref={tabsWrapperRef}
                     style={{
-                        width: `${tabsWrapperWidth}px`,
+                        width: noTabs ? "100%" : `${tabsWrapperWidth}px`,
                         ...(noTabs ? ({ WebkitAppRegion: "drag" } as React.CSSProperties) : {}),
                     }}
                 >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/app/tab/tabbar.tsx` around lines 691 - 697, The tabs-wrapper
currently uses tabsWrapperWidth computed from tabIds and tabWidthRef even when
noTabs is true, causing an empty horizontal drag region; update the div
rendering (className "tabs-wrapper") to set style.width to "100%" when noTabs is
true (instead of `${tabIds.length * tabWidthRef.current}px`) so the wrapper
fills available space; keep the existing WebkitAppRegion: "drag" behavior when
noTabs is true and only change the width logic that references
tabIds/tabWidthRef.

>
{!noTabs && tabIds.map((tabId, index) => {
const isActive = activeTabId === tabId;
const showDivider = index !== 0 && !isActive && index !== activeTabIndex + 1;
return (
<Tab
key={tabId}
ref={tabRefs.current[index]}
id={tabId}
showDivider={showDivider}
onSelect={() => handleSelectTab(tabId)}
active={isActive}
onDragStart={(event) => handleDragStart(event, tabId, tabRefs.current[index])}
onClose={(event) => handleCloseTab(event, tabId)}
onLoaded={() => handleTabLoaded(tabId)}
isDragging={draggingTab === tabId}
tabWidth={tabWidthRef.current}
isNew={tabId === newTabId}
/>
);
})}
{!noTabs &&
tabIds.map((tabId, index) => {
const isActive = activeTabId === tabId;
const showDivider = index !== 0 && !isActive && index !== activeTabIndex + 1;
return (
<Tab
key={tabId}
ref={tabRefs.current[index]}
id={tabId}
showDivider={showDivider}
onSelect={() => handleSelectTab(tabId)}
active={isActive}
onDragStart={(event) => handleDragStart(event, tabId, tabRefs.current[index])}
onClose={(event) => handleCloseTab(event, tabId)}
onLoaded={() => handleTabLoaded(tabId)}
isDragging={draggingTab === tabId}
tabWidth={tabWidthRef.current}
isNew={tabId === newTabId}
/>
);
})}
</div>
</div>
<button
Expand Down
7 changes: 6 additions & 1 deletion frontend/app/tab/tabbarenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export type TabBarEnv = WaveEnvSubset<{
installAppUpdate: WaveEnv["electron"]["installAppUpdate"];
};
rpc: {
ActivityCommand: WaveEnv["rpc"]["ActivityCommand"];
SetConfigCommand: WaveEnv["rpc"]["SetConfigCommand"];
SetMetaCommand: WaveEnv["rpc"]["SetMetaCommand"];
UpdateTabNameCommand: WaveEnv["rpc"]["UpdateTabNameCommand"];
UpdateWorkspaceTabIdsCommand: WaveEnv["rpc"]["UpdateWorkspaceTabIdsCommand"];
};
atoms: {
Expand All @@ -24,7 +28,8 @@ export type TabBarEnv = WaveEnvSubset<{
updaterStatusAtom: WaveEnv["atoms"]["updaterStatusAtom"];
};
wos: WaveEnv["wos"];
getSettingsKeyAtom: SettingsKeyAtomFnType<"app:hideaibutton" | "tab:confirmclose" | "window:showmenubar">;
getSettingsKeyAtom: SettingsKeyAtomFnType<"app:hideaibutton" | "app:tabbar" | "tab:confirmclose" | "window:showmenubar">;
showContextMenu: WaveEnv["showContextMenu"];
mockSetWaveObj: WaveEnv["mockSetWaveObj"];
isWindows: WaveEnv["isWindows"];
isMacOS: WaveEnv["isMacOS"];
Expand Down
15 changes: 14 additions & 1 deletion frontend/util/platformutil.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
// Copyright 2025, Command Line Inc.
// Copyright 2026, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

export const PlatformMacOS = "darwin";
export const PlatformWindows = "win32";
export const PlatformLinux = "linux";
export let PLATFORM: NodeJS.Platform = PlatformMacOS;
export let MacOSVersion: string = null;

export function setPlatform(platform: NodeJS.Platform) {
PLATFORM = platform;
}

export function setMacOSVersion(version: string) {
MacOSVersion = version;
}

export function isMacOSTahoeOrLater(): boolean {
if (!isMacOS() || MacOSVersion == null) {
return false;
}
const major = parseInt(MacOSVersion.split(".")[0], 10);
return major >= 16;
}

export function isMacOS(): boolean {
return PLATFORM == PlatformMacOS;
}
Expand Down
5 changes: 5 additions & 0 deletions frontend/wave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { activeTabIdAtom } from "@/store/tab-model";
import * as WOS from "@/store/wos";
import { loadFonts } from "@/util/fontutil";
import { setKeyUtilPlatform } from "@/util/keyutil";
import { isMacOS, setMacOSVersion } from "@/util/platformutil";
import { createElement } from "react";
import { createRoot } from "react-dom/client";

Expand Down Expand Up @@ -163,6 +164,10 @@ async function initWave(initOpts: WaveInitOpts) {
await loadBadges();
initGlobalWaveEventSubs(initOpts);
subscribeToConnEvents();
if (isMacOS()) {
const macOSVersion = await RpcApi.MacOSVersionCommand(TabRpcClient);
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.

WARNING: Missing error handling for MacOSVersionCommand RPC call. If this RPC fails (e.g., server not ready), it will throw an unhandled error and could crash the app initialization.

Consider wrapping this in a try-catch to gracefully handle failures:

if (isMacOS()) {
    try {
        const macOSVersion = await RpcApi.MacOSVersionCommand(TabRpcClient);
        setMacOSVersion(macOSVersion);
    } catch (e) {
        console.warn("Failed to get macOS version:", e);
    }
}

setMacOSVersion(macOSVersion);
}
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.

⚠️ Potential issue | 🟠 Major

Don’t let macOS version probing block app startup.

If MacOSVersionCommand fails on Line 168, initialization can abort even though this is non-critical metadata. Guard it so Wave still renders.

💡 Suggested fix
     if (isMacOS()) {
-        const macOSVersion = await RpcApi.MacOSVersionCommand(TabRpcClient);
-        setMacOSVersion(macOSVersion);
+        try {
+            const macOSVersion = await RpcApi.MacOSVersionCommand(TabRpcClient);
+            setMacOSVersion(macOSVersion);
+        } catch (e) {
+            getApi().sendLog("MacOSVersionCommand failed: " + (e?.message ?? e));
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/wave.ts` around lines 167 - 170, The macOS probe can throw and block
startup; wrap the call to RpcApi.MacOSVersionCommand(TabRpcClient) inside a
try/catch so failures are swallowed (logged) and do not abort
initialization—only call setMacOSVersion(macOSVersion) on success; keep the
surrounding isMacOS() check and use TabRpcClient as before, but ensure any error
is caught and handled (e.g., console/process logger) so Wave continues rendering
with a nil/default macOS version.


// ensures client/window/workspace are loaded into the cache before rendering
try {
Expand Down
6 changes: 6 additions & 0 deletions pkg/wshrpc/wshclient/wshclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,12 @@ func ListAllEditableAppsCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) ([]wshr
return resp, err
}

// command "macosversion", wshserver.MacOSVersionCommand
func MacOSVersionCommand(w *wshutil.WshRpc, opts *wshrpc.RpcOpts) (string, error) {
resp, err := sendRpcRequestCallHelper[string](w, "macosversion", nil, opts)
return resp, err
}

// command "makedraftfromlocal", wshserver.MakeDraftFromLocalCommand
func MakeDraftFromLocalCommand(w *wshutil.WshRpc, data wshrpc.CommandMakeDraftFromLocalData, opts *wshrpc.RpcOpts) (*wshrpc.CommandMakeDraftFromLocalRtnData, error) {
resp, err := sendRpcRequestCallHelper[*wshrpc.CommandMakeDraftFromLocalRtnData](w, "makedraftfromlocal", data, opts)
Expand Down
1 change: 1 addition & 0 deletions pkg/wshrpc/wshrpctypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type WshRpcInterface interface {
DebugTermCommand(ctx context.Context, data CommandDebugTermData) (*CommandDebugTermRtnData, error)
BlocksListCommand(ctx context.Context, data BlocksListRequest) ([]BlocksListEntry, error)
WaveInfoCommand(ctx context.Context) (*WaveInfoData, error)
MacOSVersionCommand(ctx context.Context) (string, error)
WshActivityCommand(ct context.Context, data map[string]int) error
ActivityCommand(ctx context.Context, data ActivityUpdate) error
RecordTEventCommand(ctx context.Context, data telemetrydata.TEvent) error
Expand Down
4 changes: 4 additions & 0 deletions pkg/wshrpc/wshserver/wshserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,10 @@ func (ws *WshServer) WaveInfoCommand(ctx context.Context) (*wshrpc.WaveInfoData,
}, nil
}

func (ws *WshServer) MacOSVersionCommand(ctx context.Context) (string, error) {
return wavebase.ClientMacOSVersion(), nil
}

// BlocksListCommand returns every block visible in the requested
// scope (current workspace by default).
func (ws *WshServer) BlocksListCommand(
Expand Down