Keep workspace focus mode scoped and easy to exit (#2151)

* fix(workspace): keep focus mode scoped and easy to exit

Route focus mode through the active workspace so persisted state cannot hide chrome on settings or other screens. Add a visible exit control and keep desktop window chrome aligned and visually quiet.

* fix(app): clarify muted chrome semantics
This commit is contained in:
Mohamed Boudra
2026-07-16 23:46:24 +02:00
committed by GitHub
parent d2308f4835
commit a622860a3e
25 changed files with 261 additions and 30 deletions
+1
View File
@@ -45,6 +45,7 @@ contextBridge.exposeInMainWorld("paseoDesktop", {
height?: number;
backgroundColor?: string;
foregroundColor?: string;
trafficLightOffsetY?: number;
}) => ipcRenderer.invoke("paseo:window:updateWindowControls", update),
onResized: (handler: EventHandler): (() => void) => {
const listener = (_ipcEvent: Electron.IpcRendererEvent, payload: unknown) => {
@@ -1,6 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import {
applyMacWindowControlsUpdate,
applyWindowControlsOverlayUpdate,
createWindowControlsOverlayState,
DEFAULT_WINDOW_HEIGHT,
@@ -70,10 +71,12 @@ describe("window-manager", () => {
readWindowControlsOverlayUpdate({
height: 48,
backgroundColor: "#181B1A",
trafficLightOffsetY: -5,
}),
).toEqual({
height: 48,
backgroundColor: "#181B1A",
trafficLightOffsetY: -5,
});
});
@@ -82,6 +85,13 @@ describe("window-manager", () => {
expect(readWindowControlsOverlayUpdate({})).toBeNull();
expect(readWindowControlsOverlayUpdate({ height: 0 })).toBeNull();
expect(readWindowControlsOverlayUpdate({ backgroundColor: 12 })).toBeNull();
expect(readWindowControlsOverlayUpdate({ trafficLightOffsetY: -11 })).toBeNull();
});
it("preserves fractional traffic-light offsets", () => {
expect(readWindowControlsOverlayUpdate({ trafficLightOffsetY: 1.5 })).toEqual({
trafficLightOffsetY: 1.5,
});
});
});
@@ -139,6 +149,24 @@ describe("window-manager", () => {
});
});
describe("applyMacWindowControlsUpdate", () => {
it("uses the focus and normal traffic-light positions", () => {
const setWindowButtonPosition = vi.fn();
applyMacWindowControlsUpdate({
win: { setWindowButtonPosition },
update: { trafficLightOffsetY: -5 },
});
applyMacWindowControlsUpdate({
win: { setWindowButtonPosition },
update: { trafficLightOffsetY: 0.5 },
});
expect(setWindowButtonPosition).toHaveBeenNthCalledWith(1, { x: 16, y: 9 });
expect(setWindowButtonPosition).toHaveBeenNthCalledWith(2, { x: 16, y: 14.5 });
});
});
describe("getMainWindowChromeOptions", () => {
it("uses frameless hidden title bars with overlay on windows", () => {
expect(
+35 -2
View File
@@ -13,6 +13,8 @@ import {
import type { WindowState, WindowStateStore } from "../settings/window-state.js";
const WINDOW_STATE_SAVE_DEBOUNCE_MS = 400;
const MAC_TRAFFIC_LIGHT_POSITION = { x: 16, y: 14 } as const;
const MAX_TRAFFIC_LIGHT_OFFSET_Y = 10;
export function readBadgeCount(input: unknown): number {
if (typeof input !== "number" || !Number.isSafeInteger(input) || input < 0) {
@@ -27,6 +29,7 @@ export interface WindowControlsOverlayUpdate {
height?: number;
backgroundColor?: string;
foregroundColor?: string;
trafficLightOffsetY?: number;
}
export interface WindowControlsOverlayState {
@@ -79,7 +82,7 @@ export function getMainWindowChromeOptions(input: {
return {
titleBarStyle: "hidden",
titleBarOverlay: true,
trafficLightPosition: { x: 16, y: 14 },
trafficLightPosition: MAC_TRAFFIC_LIGHT_POSITION,
};
}
@@ -128,6 +131,14 @@ function readOverlayColor(input: unknown): string | null {
return input;
}
function readTrafficLightOffsetY(input: unknown): number | null {
if (typeof input !== "number" || !Number.isFinite(input)) {
return null;
}
return Math.abs(input) <= MAX_TRAFFIC_LIGHT_OFFSET_Y ? input : null;
}
export function readWindowControlsOverlayUpdate(
input: unknown,
): WindowControlsOverlayUpdate | null {
@@ -139,8 +150,14 @@ export function readWindowControlsOverlayUpdate(
const height = readFiniteOverlayHeight(candidate.height);
const backgroundColor = readOverlayColor(candidate.backgroundColor);
const foregroundColor = readOverlayColor(candidate.foregroundColor);
const trafficLightOffsetY = readTrafficLightOffsetY(candidate.trafficLightOffsetY);
if (height === null && backgroundColor === null && foregroundColor === null) {
if (
height === null &&
backgroundColor === null &&
foregroundColor === null &&
trafficLightOffsetY === null
) {
return null;
}
@@ -148,6 +165,7 @@ export function readWindowControlsOverlayUpdate(
...(height !== null ? { height } : {}),
...(backgroundColor !== null ? { backgroundColor } : {}),
...(foregroundColor !== null ? { foregroundColor } : {}),
...(trafficLightOffsetY !== null ? { trafficLightOffsetY } : {}),
};
}
@@ -176,6 +194,20 @@ export function applyWindowControlsOverlayUpdate(input: {
return next;
}
export function applyMacWindowControlsUpdate(input: {
win: Pick<BrowserWindow, "setWindowButtonPosition">;
update: WindowControlsOverlayUpdate;
}): void {
if (input.update.trafficLightOffsetY === undefined) {
return;
}
input.win.setWindowButtonPosition({
x: MAC_TRAFFIC_LIGHT_POSITION.x,
y: MAC_TRAFFIC_LIGHT_POSITION.y + input.update.trafficLightOffsetY,
});
}
export function registerWindowManager(): void {
const overlayStateByWindow = new WeakMap<BrowserWindow, WindowControlsOverlayState>();
@@ -230,6 +262,7 @@ export function registerWindowManager(): void {
}
if (process.platform === "darwin") {
applyMacWindowControlsUpdate({ win, update: nextUpdate });
return;
}