-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add terminal font size zoom with Cmd+/- shortcuts #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,3 +31,4 @@ coverage/ | |
| # Temporary files | ||
| *.tmp | ||
| .cache/ | ||
| .worktrees | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| /** | ||
| * TerminalFontSize - Global terminal font size with Cmd+/- zoom support | ||
| * | ||
| * Persists the font size in localStorage and updates all active terminals. | ||
| */ | ||
|
|
||
| import { createSignal } from 'solid-js'; | ||
| import { terminalPool } from './TerminalPool'; | ||
| import { keyboardShortcutManager } from './KeyboardShortcutManager'; | ||
|
|
||
| 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', | ||
| }); | ||
|
Comment on lines
+11
to
+116
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TerminalFontSizeimportsterminalPool, whileTerminalPoolimportsgetTerminalFontSizefromTerminalFontSize, 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 havingTerminalPoolobserveterminalFontSize()(e.g., a Solid effect in the pool module) and apply updates to its handles, or by introducing a lightweight callback/subscription interface soTerminalFontSizedoesn’t importTerminalPooldirectly.