Unverified 提交 f04a83d0 authored 作者: Mohamed Aziz Mejri's avatar Mohamed Aziz Mejri 提交者: GitHub

Adding a shortcut for zooming (#2562)

<!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2562" target="_blank"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds keyboard shortcuts for zooming (Ctrl/Cmd + =, -, 0) and ties zoom to user settings for consistent behavior across the app. Moves zoom handling from the Electron menu to a client-side React hook. - **New Features** - Added useZoomShortcuts to handle zoom in/out/reset and update settings.zoomLevel. - Supports levels 90, 100, 110, 125, 150 (default 100) on Mac (Cmd) and Windows/Linux (Ctrl). - **Refactors** - Centralized ZOOM_LEVELS and DEFAULT_ZOOM_LEVEL in schemas, derived from ZoomLevelSchema.options. - Removed Electron zoom roles and initialized the shortcut hook in RootLayout. - Prevent shortcuts from firing in inputs, textareas, and contenteditable elements. <sup>Written for commit cb368231e86d859a8e7397d237bb86e460ca7023. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 efcc2958
...@@ -13,12 +13,11 @@ import { ...@@ -13,12 +13,11 @@ import {
selectedAppIdAtom, selectedAppIdAtom,
} from "@/atoms/appAtoms"; } from "@/atoms/appAtoms";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import type { ZoomLevel } from "@/lib/schemas"; import { DEFAULT_ZOOM_LEVEL } from "@/lib/schemas";
import { selectedComponentsPreviewAtom } from "@/atoms/previewAtoms"; import { selectedComponentsPreviewAtom } from "@/atoms/previewAtoms";
import { chatInputValueAtom } from "@/atoms/chatAtoms"; import { chatInputValueAtom } from "@/atoms/chatAtoms";
import { usePlanEvents } from "@/hooks/usePlanEvents"; import { usePlanEvents } from "@/hooks/usePlanEvents";
import { useZoomShortcuts } from "@/hooks/useZoomShortcuts";
const DEFAULT_ZOOM_LEVEL: ZoomLevel = "100";
export default function RootLayout({ children }: { children: ReactNode }) { export default function RootLayout({ children }: { children: ReactNode }) {
const { refreshAppIframe } = useRunApp(); const { refreshAppIframe } = useRunApp();
...@@ -36,6 +35,9 @@ export default function RootLayout({ children }: { children: ReactNode }) { ...@@ -36,6 +35,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
// Initialize plan events listener // Initialize plan events listener
usePlanEvents(); usePlanEvents();
// Zoom keyboard shortcuts (Ctrl/Cmd + =/- /0)
useZoomShortcuts();
useEffect(() => { useEffect(() => {
const zoomLevel = settings?.zoomLevel ?? DEFAULT_ZOOM_LEVEL; const zoomLevel = settings?.zoomLevel ?? DEFAULT_ZOOM_LEVEL;
const zoomFactor = Number(zoomLevel) / 100; const zoomFactor = Number(zoomLevel) / 100;
......
import { useMemo } from "react"; import { useMemo } from "react";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { ZoomLevel, ZoomLevelSchema } from "@/lib/schemas"; import { ZoomLevel, ZoomLevelSchema, DEFAULT_ZOOM_LEVEL } from "@/lib/schemas";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { import {
Select, Select,
...@@ -26,8 +26,6 @@ const ZOOM_LEVEL_DESCRIPTIONS: Record<ZoomLevel, string> = { ...@@ -26,8 +26,6 @@ const ZOOM_LEVEL_DESCRIPTIONS: Record<ZoomLevel, string> = {
"150": "Maximum zoom for maximum accessibility.", "150": "Maximum zoom for maximum accessibility.",
}; };
const DEFAULT_ZOOM_LEVEL: ZoomLevel = "100";
export function ZoomSelector() { export function ZoomSelector() {
const { settings, updateSettings } = useSettings(); const { settings, updateSettings } = useSettings();
const currentZoomLevel: ZoomLevel = useMemo(() => { const currentZoomLevel: ZoomLevel = useMemo(() => {
......
import { useCallback, useEffect, useMemo } from "react";
import { useSettings } from "@/hooks/useSettings";
import {
type ZoomLevel,
ZoomLevelSchema,
ZOOM_LEVELS,
DEFAULT_ZOOM_LEVEL,
} from "@/lib/schemas";
import { useIsMac } from "@/hooks/useChatModeToggle";
export function useZoomShortcuts() {
const { settings, updateSettings } = useSettings();
const isMac = useIsMac();
const currentZoomLevel: ZoomLevel = useMemo(() => {
const value = settings?.zoomLevel ?? DEFAULT_ZOOM_LEVEL;
return ZoomLevelSchema.safeParse(value).success
? (value as ZoomLevel)
: DEFAULT_ZOOM_LEVEL;
}, [settings?.zoomLevel]);
const zoomIn = useCallback(() => {
const currentIndex = ZOOM_LEVELS.indexOf(currentZoomLevel);
if (currentIndex < ZOOM_LEVELS.length - 1) {
updateSettings({ zoomLevel: ZOOM_LEVELS[currentIndex + 1] });
}
}, [currentZoomLevel, updateSettings]);
const zoomOut = useCallback(() => {
const currentIndex = ZOOM_LEVELS.indexOf(currentZoomLevel);
if (currentIndex > 0) {
updateSettings({ zoomLevel: ZOOM_LEVELS[currentIndex - 1] });
}
}, [currentZoomLevel, updateSettings]);
const resetZoom = useCallback(() => {
updateSettings({ zoomLevel: DEFAULT_ZOOM_LEVEL });
}, [updateSettings]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const target = event.target as HTMLElement;
if (
target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement ||
target.isContentEditable
) {
return;
}
const modifier = isMac ? event.metaKey : event.ctrlKey;
if (!modifier) return;
if (event.key === "=" || event.key === "+") {
event.preventDefault();
zoomIn();
} else if (event.key === "-") {
event.preventDefault();
zoomOut();
} else if (event.key === "0") {
event.preventDefault();
resetZoom();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [isMac, zoomIn, zoomOut, resetZoom]);
}
...@@ -241,6 +241,8 @@ export type ReleaseChannel = z.infer<typeof ReleaseChannelSchema>; ...@@ -241,6 +241,8 @@ export type ReleaseChannel = z.infer<typeof ReleaseChannelSchema>;
export const ZoomLevelSchema = z.enum(["90", "100", "110", "125", "150"]); export const ZoomLevelSchema = z.enum(["90", "100", "110", "125", "150"]);
export type ZoomLevel = z.infer<typeof ZoomLevelSchema>; export type ZoomLevel = z.infer<typeof ZoomLevelSchema>;
export const ZOOM_LEVELS: readonly ZoomLevel[] = ZoomLevelSchema.options;
export const DEFAULT_ZOOM_LEVEL: ZoomLevel = "100";
export const DeviceModeSchema = z.enum(["desktop", "tablet", "mobile"]); export const DeviceModeSchema = z.enum(["desktop", "tablet", "mobile"]);
export type DeviceMode = z.infer<typeof DeviceModeSchema>; export type DeviceMode = z.infer<typeof DeviceModeSchema>;
......
...@@ -351,10 +351,6 @@ const createApplicationMenu = () => { ...@@ -351,10 +351,6 @@ const createApplicationMenu = () => {
? [{ role: "toggleDevTools" as const }] ? [{ role: "toggleDevTools" as const }]
: []), : []),
{ type: "separator" as const }, { type: "separator" as const },
{ role: "resetZoom" as const },
{ role: "zoomIn" as const },
{ role: "zoomOut" as const },
{ type: "separator" as const },
{ role: "togglefullscreen" as const }, { role: "togglefullscreen" as const },
], ],
}, },
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论