Skip to content

feat: add terminal font size zoom with Cmd+/- shortcuts#5

Open
stromseth wants to merge 1 commit into
faiyaz26:mainfrom
stromseth:feat/terminal-font-size-zoom
Open

feat: add terminal font size zoom with Cmd+/- shortcuts#5
stromseth wants to merge 1 commit into
faiyaz26:mainfrom
stromseth:feat/terminal-font-size-zoom

Conversation

@stromseth
Copy link
Copy Markdown

Summary

  • Cmd+= or Cmd++ increases terminal font size
  • Cmd+- decreases terminal font size
  • Cmd+0 resets to default (13px)
  • Font size persists between sessions via localStorage (range: 8–32px)
  • Both = and + keys registered for zoom-in to support Norwegian and other keyboard layouts where + is a standalone key (not Shift+=)

Implementation

New file: frontend/src/services/TerminalFontSize.ts

  • SolidJS signal for reactive state
  • localStorage persistence with range validation
  • Applies to all active terminals via TerminalPool.getAllHandles()
  • Resizes PTY after fitAddon.fit() to keep cols/rows in sync
  • Registers shortcuts via KeyboardShortcutManager

Modified files:

  • packages/shared/src/terminal/terminal-utils.tscreateTerminal() accepts optional fontSize param; attachCustomKeyEventHandler lets Cmd+/-/0/= bubble to DOM instead of being sent to the PTY
  • frontend/src/services/TerminalPool.ts — Uses current font size when creating new terminals
  • frontend/src/hooks/useCodeReviewTerminal.ts — Uses current font size at creation and reacts to runtime changes via createEffect
  • frontend/src/App.tsx — Side-effect import to register shortcuts at startup

Test plan

  • Cmd+= increases font size in active terminal
  • Cmd++ increases font size (Norwegian/non-US keyboards)
  • Cmd+- decreases font size
  • Cmd+0 resets to default
  • Font size persists after app restart
  • All frontend tests pass: cd frontend && pnpm test

Adds keyboard shortcuts to increase/decrease terminal font size:
- Cmd+= or Cmd++ to increase font size
- Cmd+- to decrease font size
- Cmd+0 to reset to default (13px)

Both = and + keys are registered for zoom-in to support Norwegian
and other keyboard layouts where + is a standalone key.

Font size is persisted via localStorage (range: 8-32px) and applied
to all active terminal instances including the code review terminal.
The PTY is resized after each font change to keep cols/rows in sync.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@stromseth stromseth marked this pull request as ready for review April 9, 2026 20:10
@faiyaz26 faiyaz26 requested a review from Copilot April 14, 2026 09:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds global terminal font-size zoom support (Cmd/Ctrl =, +, -, 0) with persistence, and wires it into terminal creation so new terminals start at the configured size.

Changes:

  • Introduces TerminalFontSize service to persist/clamp font size and register global zoom shortcuts.
  • Propagates font size into terminal creation (TerminalPool, code review terminal) and allows zoom keys to bubble past xterm key handling.
  • Updates ignore rules to exclude .worktrees.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shared/src/terminal/terminal-utils.ts Adds fontSize parameter to createTerminal() and lets zoom key combos pass through xterm’s custom key handler.
frontend/src/services/TerminalPool.ts Uses the current global terminal font size when creating pooled terminals.
frontend/src/services/TerminalFontSize.ts New global font size state + localStorage persistence + shortcut registration and fan-out to active terminals.
frontend/src/hooks/useCodeReviewTerminal.ts Initializes code-review terminal with global font size and reacts to font-size changes.
frontend/src/App.tsx Side-effect import to register terminal zoom shortcuts at startup.
.gitignore Ignores .worktrees.
Comments suppressed due to low confidence (1)

frontend/src/hooks/useCodeReviewTerminal.ts:69

  • The terminal created here doesn’t attach the shared attachKeyHandlers/attachCustomKeyEventHandler logic that prevents Cmd/Ctrl + =/-/0 from being processed by xterm. With the new global zoom shortcuts, keydown events will reach xterm before the window-level KeyboardShortcutManager handler, so the zoom keystrokes may still be sent to the PTY while also changing font size. Consider reusing attachKeyHandlers (or at least adding a attachCustomKeyEventHandler guard for these combos) for this terminal too.
    terminal = new Terminal({
      theme: {
        background: '#111111',
        foreground: '#e6e6e6',
      },
      fontFamily: 'Menlo, Monaco, "Courier New", monospace',
      fontSize: getTerminalFontSize(),
      cursorBlink: true,
      allowProposedApi: true, // Required for Unicode11 addon
    });

    fitAddon = new FitAddon();
    terminal.loadAddon(fitAddon);
    terminal.open(container);
    loadAddons(terminal);
    fitAddon.fit();


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +7 to +9
import { createSignal } from 'solid-js';
import { terminalPool } from './TerminalPool';
import { keyboardShortcutManager } from './KeyboardShortcutManager';
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TerminalFontSize imports terminalPool, while TerminalPool imports getTerminalFontSize from TerminalFontSize, creating a circular module dependency. This is fragile (can yield partially-initialized exports under different import orders/HMR/tests) and also couples the font-size service to the pool implementation. Consider breaking the cycle by having TerminalPool observe terminalFontSize() (e.g., a Solid effect in the pool module) and apply updates to its handles, or by introducing a lightweight callback/subscription interface so TerminalFontSize doesn’t import TerminalPool directly.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +116
const STORAGE_KEY = 'codelane-terminal-font-size';
const DEFAULT_FONT_SIZE = 13;
const MIN_FONT_SIZE = 8;
const MAX_FONT_SIZE = 32;
const STEP = 1;

function loadFontSize(): number {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
const parsed = parseInt(stored, 10);
if (!isNaN(parsed) && parsed >= MIN_FONT_SIZE && parsed <= MAX_FONT_SIZE) {
return parsed;
}
}
} catch {
// localStorage unavailable
}
return DEFAULT_FONT_SIZE;
}

function saveFontSize(size: number): void {
try {
localStorage.setItem(STORAGE_KEY, String(size));
} catch {
// localStorage unavailable
}
}

const [fontSize, setFontSizeSignal] = createSignal(loadFontSize());

function applyToAllTerminals(size: number): void {
for (const handle of terminalPool.getAllHandles()) {
handle.terminal.options.fontSize = size;
try {
handle.fitAddon.fit();
// Resize PTY to match new cols/rows after font size change
terminalPool.resize(handle.id, handle.terminal.cols, handle.terminal.rows).catch(() => {});
} catch {
// terminal may not be attached yet
}
}
}

export function getTerminalFontSize(): number {
return fontSize();
}

/** Reactive signal accessor for use in SolidJS effects */
export const terminalFontSize = fontSize;

export function setTerminalFontSize(size: number): void {
const clamped = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, size));
setFontSizeSignal(clamped);
saveFontSize(clamped);
applyToAllTerminals(clamped);
}

export function increaseTerminalFontSize(): void {
setTerminalFontSize(fontSize() + STEP);
}

export function decreaseTerminalFontSize(): void {
setTerminalFontSize(fontSize() - STEP);
}

export function resetTerminalFontSize(): void {
setTerminalFontSize(DEFAULT_FONT_SIZE);
}

// Register global keyboard shortcuts for terminal zoom
keyboardShortcutManager.register({
id: 'terminalZoomIn',
description: 'Increase terminal font size',
key: '=',
modifiers: { cmdOrCtrl: true },
handler: () => increaseTerminalFontSize(),
scope: 'global',
});

keyboardShortcutManager.register({
id: 'terminalZoomInPlus',
description: 'Increase terminal font size (+)',
key: '+',
modifiers: { cmdOrCtrl: true },
handler: () => increaseTerminalFontSize(),
scope: 'global',
});

keyboardShortcutManager.register({
id: 'terminalZoomOut',
description: 'Decrease terminal font size',
key: '-',
modifiers: { cmdOrCtrl: true },
handler: () => decreaseTerminalFontSize(),
scope: 'global',
});

keyboardShortcutManager.register({
id: 'terminalZoomReset',
description: 'Reset terminal font size',
key: '0',
modifiers: { cmdOrCtrl: true },
handler: () => resetTerminalFontSize(),
scope: 'global',
});
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file registers global zoom shortcuts, but there are no unit tests covering the persistence/clamping behavior (8–32px) or that the Cmd/Ctrl shortcuts actually update state. Since the repo already has service-level tests for KeyboardShortcutManager, consider adding tests for TerminalFontSize (e.g., mocking localStorage and verifying increase/decrease/reset + shortcut dispatch update the stored value).

Copilot uses AI. Check for mistakes.
@faiyaz26
Copy link
Copy Markdown
Owner

Hello @stromseth thanks for your contribution, could you add some unit tests and resolve the comments above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants