From 07d5f5a1338b90bac2d75d6493b9652a4a389666 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Tue, 14 Jul 2026 01:14:24 +0800 Subject: [PATCH] Add terminal font size shortcuts (#1047) --- src/ui/features/terminal/Terminal.tsx | 24 +++--- .../terminal/terminal-font-zoom.test.ts | 74 +++++++++++++++++++ .../features/terminal/terminal-font-zoom.ts | 37 ++++++++++ 3 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 src/ui/features/terminal/terminal-font-zoom.test.ts create mode 100644 src/ui/features/terminal/terminal-font-zoom.ts diff --git a/src/ui/features/terminal/Terminal.tsx b/src/ui/features/terminal/Terminal.tsx index d1dd71e3..3e975b3a 100644 --- a/src/ui/features/terminal/Terminal.tsx +++ b/src/ui/features/terminal/Terminal.tsx @@ -57,6 +57,10 @@ import { toast } from "sonner"; import { Button } from "@/components/button"; import { resolveTermixThemeColors } from "./terminal-theme.ts"; import type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts"; +import { + getNextTerminalFontSize, + getTerminalFontZoomDirection, +} from "./terminal-font-zoom.ts"; export type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts"; type HostKeyVerificationData = Omit< @@ -83,8 +87,6 @@ interface SSHTerminalProps { disableAutoFocus?: boolean; } -const TERMINAL_FONT_ZOOM_MIN = 8; -const TERMINAL_FONT_ZOOM_MAX = 36; const ALTERNATE_SCREEN_SEQUENCE = /\x1b\[\?(47|1047|1049)([hl])/g; function updateAlternateScreenMode(output: string, currentMode: boolean) { @@ -497,16 +499,12 @@ const TerminalInner = forwardRef( } } - function zoomTerminalFont(deltaY: number) { - const direction = deltaY < 0 ? 1 : -1; + function changeTerminalFontSize(direction: -1 | 1) { const currentFontSize = terminal.options.fontSize ?? terminalFontSizeRef.current ?? DEFAULT_TERMINAL_CONFIG.fontSize; - const nextFontSize = Math.min( - TERMINAL_FONT_ZOOM_MAX, - Math.max(TERMINAL_FONT_ZOOM_MIN, currentFontSize + direction), - ); + const nextFontSize = getNextTerminalFontSize(currentFontSize, direction); if (nextFontSize === currentFontSize) { return; @@ -2167,7 +2165,7 @@ const TerminalInner = forwardRef( terminal.attachCustomWheelEventHandler((ev) => { if (ev.ctrlKey || ev.metaKey) { - zoomTerminalFont(ev.deltaY); + changeTerminalFontSize(ev.deltaY < 0 ? 1 : -1); return false; } @@ -2398,6 +2396,14 @@ const TerminalInner = forwardRef( } } + const fontZoomDirection = getTerminalFontZoomDirection(e); + if (fontZoomDirection !== 0) { + e.preventDefault(); + e.stopPropagation(); + changeTerminalFontSize(fontZoomDirection); + return false; + } + if ( e.ctrlKey && !e.shiftKey && diff --git a/src/ui/features/terminal/terminal-font-zoom.test.ts b/src/ui/features/terminal/terminal-font-zoom.test.ts new file mode 100644 index 00000000..7b83c187 --- /dev/null +++ b/src/ui/features/terminal/terminal-font-zoom.test.ts @@ -0,0 +1,74 @@ +import { describe, expect, it } from "vitest"; +import { + getNextTerminalFontSize, + getTerminalFontZoomDirection, +} from "./terminal-font-zoom"; + +function keyEvent( + overrides: Partial, +): Pick { + return { + altKey: false, + code: "", + ctrlKey: false, + key: "", + metaKey: false, + ...overrides, + }; +} + +describe("getTerminalFontZoomDirection", () => { + it("recognizes Ctrl+Plus and Ctrl+Minus", () => { + expect( + getTerminalFontZoomDirection( + keyEvent({ ctrlKey: true, code: "Equal", key: "+" }), + ), + ).toBe(1); + expect( + getTerminalFontZoomDirection( + keyEvent({ ctrlKey: true, code: "Minus", key: "-" }), + ), + ).toBe(-1); + }); + + it("recognizes unshifted equal, numpad, and macOS shortcuts", () => { + expect( + getTerminalFontZoomDirection( + keyEvent({ ctrlKey: true, code: "Equal", key: "=" }), + ), + ).toBe(1); + expect( + getTerminalFontZoomDirection( + keyEvent({ ctrlKey: true, code: "NumpadAdd", key: "+" }), + ), + ).toBe(1); + expect( + getTerminalFontZoomDirection( + keyEvent({ metaKey: true, code: "NumpadSubtract", key: "-" }), + ), + ).toBe(-1); + }); + + it("ignores unmodified and Alt-modified keys", () => { + expect( + getTerminalFontZoomDirection(keyEvent({ code: "Equal", key: "+" })), + ).toBe(0); + expect( + getTerminalFontZoomDirection( + keyEvent({ altKey: true, ctrlKey: true, code: "Minus", key: "-" }), + ), + ).toBe(0); + }); +}); + +describe("getNextTerminalFontSize", () => { + it("changes the font size one pixel at a time", () => { + expect(getNextTerminalFontSize(14, 1)).toBe(15); + expect(getNextTerminalFontSize(14, -1)).toBe(13); + }); + + it("keeps the existing zoom limits", () => { + expect(getNextTerminalFontSize(36, 1)).toBe(36); + expect(getNextTerminalFontSize(8, -1)).toBe(8); + }); +}); diff --git a/src/ui/features/terminal/terminal-font-zoom.ts b/src/ui/features/terminal/terminal-font-zoom.ts new file mode 100644 index 00000000..72dd6676 --- /dev/null +++ b/src/ui/features/terminal/terminal-font-zoom.ts @@ -0,0 +1,37 @@ +export const TERMINAL_FONT_ZOOM_MIN = 8; +export const TERMINAL_FONT_ZOOM_MAX = 36; + +type ZoomKeyEvent = Pick< + KeyboardEvent, + "altKey" | "code" | "ctrlKey" | "key" | "metaKey" +>; + +export function getTerminalFontZoomDirection(event: ZoomKeyEvent): -1 | 0 | 1 { + if ((!event.ctrlKey && !event.metaKey) || event.altKey) return 0; + + if ( + event.key === "+" || + event.code === "Equal" || + event.code === "NumpadAdd" + ) { + return 1; + } + if ( + event.key === "-" || + event.code === "Minus" || + event.code === "NumpadSubtract" + ) { + return -1; + } + return 0; +} + +export function getNextTerminalFontSize( + currentFontSize: number, + direction: -1 | 1, +): number { + return Math.min( + TERMINAL_FONT_ZOOM_MAX, + Math.max(TERMINAL_FONT_ZOOM_MIN, currentFontSize + direction), + ); +}