feat(desktop): add Cmd++ zoom shortcut (#174)

This commit is contained in:
Wes
2026-03-25 10:28:23 -07:00
committed by GitHub
parent c2f7b7a2a1
commit 84fbf61d96
5 changed files with 139 additions and 1 deletions
@@ -5,6 +5,7 @@
"windows": ["main"],
"permissions": [
"core:default",
"core:webview:allow-set-webview-zoom",
"core:window:allow-start-dragging",
"core:window:allow-show",
"core:window:allow-close",
+2 -1
View File
@@ -58,10 +58,12 @@ import {
SidebarProvider,
SidebarTrigger,
} from "@/shared/ui/sidebar";
import { useWebviewZoomShortcuts } from "@/app/useWebviewZoomShortcuts";
type AppView = "home" | "channel" | "settings" | "agents";
type MainView = Exclude<AppView, "settings">;
export function AppShell() {
useWebviewZoomShortcuts();
const [selectedView, setSelectedView] = React.useState<AppView>("home");
const [settingsSection, setSettingsSection] = React.useState<SettingsSection>(
DEFAULT_SETTINGS_SECTION,
@@ -182,7 +184,6 @@ export function AppShell() {
),
[messageProfilesQuery.data?.profiles, profileQuery.data],
);
const timelineMessages = React.useMemo(
() =>
formatTimelineMessages(
@@ -0,0 +1,90 @@
import * as React from "react";
import { getCurrentWebview } from "@tauri-apps/api/webview";
const DEFAULT_ZOOM_FACTOR = 1;
const MIN_ZOOM_FACTOR = 0.2;
const MAX_ZOOM_FACTOR = 10;
const ZOOM_STEP = 0.2;
type ZoomAction = "increase" | "decrease" | "reset";
function getZoomAction(event: KeyboardEvent): ZoomAction | null {
if (!(event.metaKey || event.ctrlKey) || event.altKey) {
return null;
}
if (
event.key === "+" ||
event.key === "=" ||
event.code === "Equal" ||
event.code === "NumpadAdd"
) {
return "increase";
}
if (
!event.shiftKey &&
(event.key === "-" ||
event.code === "Minus" ||
event.code === "NumpadSubtract")
) {
return "decrease";
}
if (
!event.shiftKey &&
(event.key === "0" || event.code === "Digit0" || event.code === "Numpad0")
) {
return "reset";
}
return null;
}
function getNextZoomFactor(action: ZoomAction, zoomFactor: number) {
if (action === "reset") {
return DEFAULT_ZOOM_FACTOR;
}
if (action === "increase") {
return Math.min(zoomFactor + ZOOM_STEP, MAX_ZOOM_FACTOR);
}
return Math.max(zoomFactor - ZOOM_STEP, MIN_ZOOM_FACTOR);
}
export function useWebviewZoomShortcuts() {
const zoomFactorRef = React.useRef(DEFAULT_ZOOM_FACTOR);
React.useEffect(() => {
const webview = getCurrentWebview();
function handleKeyDown(event: KeyboardEvent) {
const action = getZoomAction(event);
if (!action) {
return;
}
event.preventDefault();
const previousZoomFactor = zoomFactorRef.current;
const nextZoomFactor = getNextZoomFactor(action, previousZoomFactor);
if (nextZoomFactor === previousZoomFactor) {
return;
}
zoomFactorRef.current = nextZoomFactor;
void webview.setZoom(nextZoomFactor).catch((error) => {
zoomFactorRef.current = previousZoomFactor;
console.error("Failed to update webview zoom", error);
});
}
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, []);
}
+7
View File
@@ -316,6 +316,7 @@ declare global {
interface Window {
__SPROUT_E2E__?: E2eConfig;
__SPROUT_E2E_COMMANDS__?: string[];
__SPROUT_E2E_WEBVIEW_ZOOM__?: number;
__SPROUT_E2E_EMIT_MOCK_MESSAGE__?: (input: {
channelName: string;
content: string;
@@ -3207,6 +3208,7 @@ export function maybeInstallE2eTauriMocks() {
resetMockPersonas();
mockWindows("main");
window.__SPROUT_E2E_COMMANDS__ = [];
window.__SPROUT_E2E_WEBVIEW_ZOOM__ = 1;
window.__SPROUT_E2E_EMIT_MOCK_MESSAGE__ = ({
channelName,
content,
@@ -3542,6 +3544,11 @@ export function maybeInstallE2eTauriMocks() {
}
return disconnectMockSocket((payload as { id: number }).id);
case "plugin:webview|set_webview_zoom":
window.__SPROUT_E2E_WEBVIEW_ZOOM__ = (
payload as { value: number }
).value;
return;
default:
throw new Error(`Unsupported mocked Tauri command: ${command}`);
}
+39
View File
@@ -215,6 +215,45 @@ test("opens settings with the keyboard shortcut and updates theme", async ({
await expect(page.getByTestId("chat-title")).toHaveText("Home");
});
test("supports webview zoom keyboard shortcuts", async ({ page }) => {
await page.goto("/");
await page.keyboard.press(
process.platform === "darwin" ? "Meta+Shift+Equal" : "Control+Shift+Equal",
);
await expect
.poll(() => page.evaluate(() => window.__SPROUT_E2E_WEBVIEW_ZOOM__))
.toBe(1.2);
await page.keyboard.press(
process.platform === "darwin" ? "Meta+Minus" : "Control+Minus",
);
await expect
.poll(() => page.evaluate(() => window.__SPROUT_E2E_WEBVIEW_ZOOM__))
.toBe(1);
await page.keyboard.press(
process.platform === "darwin" ? "Meta+Shift+Equal" : "Control+Shift+Equal",
);
await page.keyboard.press(
process.platform === "darwin" ? "Meta+Shift+Equal" : "Control+Shift+Equal",
);
await expect
.poll(() => page.evaluate(() => window.__SPROUT_E2E_WEBVIEW_ZOOM__))
.toBe(1.4);
await page.keyboard.press(
process.platform === "darwin" ? "Meta+Digit0" : "Control+Digit0",
);
await expect
.poll(() => page.evaluate(() => window.__SPROUT_E2E_WEBVIEW_ZOOM__))
.toBe(1);
});
test("shows doctor checks for local sprout tooling", async ({ page }) => {
await page.goto("/");