mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
feat: align terminal font settings with appearance fonts
- Terminal font dropdown now uses mono fonts from UI font options - Unified font list between appearance section and terminal settings - Terminal font persisted to GlobalSettings for import/export support - Aligned global terminal settings popover with per-terminal popover: - Same settings in same order (Font Size, Run on New Terminal, Font Family, Scrollback, Line Height, Screen Reader) - Consistent styling (Radix Select instead of native select) - Added terminal padding (12px vertical, 16px horizontal) for readability
This commit is contained in:
@@ -2,11 +2,19 @@ import { Label } from '@/components/ui/label';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Slider } from '@/components/ui/slider';
|
import { Slider } from '@/components/ui/slider';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
import { SquareTerminal } from 'lucide-react';
|
import { SquareTerminal } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes';
|
import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes';
|
||||||
|
import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options';
|
||||||
|
|
||||||
export function TerminalSection() {
|
export function TerminalSection() {
|
||||||
const {
|
const {
|
||||||
@@ -53,27 +61,32 @@ export function TerminalSection() {
|
|||||||
{/* Font Family */}
|
{/* Font Family */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label className="text-foreground font-medium">Font Family</Label>
|
<Label className="text-foreground font-medium">Font Family</Label>
|
||||||
<select
|
<Select
|
||||||
value={fontFamily}
|
value={fontFamily || DEFAULT_FONT_VALUE}
|
||||||
onChange={(e) => {
|
onValueChange={(value) => {
|
||||||
setTerminalFontFamily(e.target.value);
|
setTerminalFontFamily(value);
|
||||||
toast.info('Font family changed', {
|
toast.info('Font family changed', {
|
||||||
description: 'Restart terminal for changes to take effect',
|
description: 'Restart terminal for changes to take effect',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className={cn(
|
|
||||||
'w-full px-3 py-2 rounded-lg',
|
|
||||||
'bg-accent/30 border border-border/50',
|
|
||||||
'text-foreground text-sm',
|
|
||||||
'focus:outline-none focus:ring-2 focus:ring-green-500/30'
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{TERMINAL_FONT_OPTIONS.map((font) => (
|
<SelectTrigger className="w-full">
|
||||||
<option key={font.value} value={font.value}>
|
<SelectValue placeholder="Default (Menlo / Monaco)" />
|
||||||
{font.label}
|
</SelectTrigger>
|
||||||
</option>
|
<SelectContent>
|
||||||
))}
|
{TERMINAL_FONT_OPTIONS.map((option) => (
|
||||||
</select>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily: option.value === DEFAULT_FONT_VALUE ? undefined : option.value,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Default Font Size */}
|
{/* Default Font Size */}
|
||||||
|
|||||||
@@ -25,8 +25,17 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Slider } from '@/components/ui/slider';
|
import { Slider } from '@/components/ui/slider';
|
||||||
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes';
|
import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes';
|
||||||
|
import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
|
||||||
import { TerminalPanel } from './terminal-view/terminal-panel';
|
import { TerminalPanel } from './terminal-view/terminal-panel';
|
||||||
@@ -232,6 +241,8 @@ export function TerminalView() {
|
|||||||
setTerminalDefaultRunScript,
|
setTerminalDefaultRunScript,
|
||||||
setTerminalFontFamily,
|
setTerminalFontFamily,
|
||||||
setTerminalLineHeight,
|
setTerminalLineHeight,
|
||||||
|
setTerminalScrollbackLines,
|
||||||
|
setTerminalScreenReaderMode,
|
||||||
updateTerminalPanelSizes,
|
updateTerminalPanelSizes,
|
||||||
} = useAppStore();
|
} = useAppStore();
|
||||||
|
|
||||||
@@ -1457,9 +1468,9 @@ export function TerminalView() {
|
|||||||
<Settings className="h-4 w-4" />
|
<Settings className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="w-80" align="end">
|
<PopoverContent className="w-72" align="end">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-1">
|
||||||
<h4 className="font-medium text-sm">Terminal Settings</h4>
|
<h4 className="font-medium text-sm">Terminal Settings</h4>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Configure global terminal appearance
|
Configure global terminal appearance
|
||||||
@@ -1469,15 +1480,15 @@ export function TerminalView() {
|
|||||||
{/* Default Font Size */}
|
{/* Default Font Size */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label className="text-sm">Default Font Size</Label>
|
<Label className="text-xs font-medium">Default Font Size</Label>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{terminalState.defaultFontSize}px
|
{terminalState.defaultFontSize}px
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
<Slider
|
||||||
value={[terminalState.defaultFontSize]}
|
value={[terminalState.defaultFontSize]}
|
||||||
min={8}
|
min={8}
|
||||||
max={24}
|
max={32}
|
||||||
step={1}
|
step={1}
|
||||||
onValueChange={([value]) => setTerminalDefaultFontSize(value)}
|
onValueChange={([value]) => setTerminalDefaultFontSize(value)}
|
||||||
onValueCommit={() => {
|
onValueCommit={() => {
|
||||||
@@ -1488,37 +1499,79 @@ export function TerminalView() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Default Run Script */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label className="text-xs font-medium">Run on New Terminal</Label>
|
||||||
|
<Input
|
||||||
|
value={terminalState.defaultRunScript}
|
||||||
|
onChange={(e) => setTerminalDefaultRunScript(e.target.value)}
|
||||||
|
placeholder="e.g., claude"
|
||||||
|
className="h-7 text-xs"
|
||||||
|
/>
|
||||||
|
<p className="text-[10px] text-muted-foreground">
|
||||||
|
Command to run when creating a new terminal
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Font Family */}
|
{/* Font Family */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-sm">Font Family</Label>
|
<Label className="text-xs font-medium">Font Family</Label>
|
||||||
<select
|
<Select
|
||||||
value={terminalState.fontFamily}
|
value={terminalState.fontFamily || DEFAULT_FONT_VALUE}
|
||||||
onChange={(e) => {
|
onValueChange={(value) => {
|
||||||
setTerminalFontFamily(e.target.value);
|
setTerminalFontFamily(value);
|
||||||
toast.info('Font family changed', {
|
toast.info('Font family changed', {
|
||||||
description: 'Restart terminal for changes to take effect',
|
description: 'Restart terminal for changes to take effect',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className={cn(
|
|
||||||
'w-full px-2 py-1.5 rounded-md text-sm',
|
|
||||||
'bg-accent/50 border border-border',
|
|
||||||
'text-foreground',
|
|
||||||
'focus:outline-none focus:ring-2 focus:ring-ring'
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
{TERMINAL_FONT_OPTIONS.map((font) => (
|
<SelectTrigger className="w-full h-8 text-xs">
|
||||||
<option key={font.value} value={font.value}>
|
<SelectValue placeholder="Default (Menlo / Monaco)" />
|
||||||
{font.label}
|
</SelectTrigger>
|
||||||
</option>
|
<SelectContent>
|
||||||
))}
|
{TERMINAL_FONT_OPTIONS.map((option) => (
|
||||||
</select>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily:
|
||||||
|
option.value === DEFAULT_FONT_VALUE ? undefined : option.value,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Scrollback */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label className="text-xs font-medium">Scrollback</Label>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
{(terminalState.scrollbackLines / 1000).toFixed(0)}k lines
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Slider
|
||||||
|
value={[terminalState.scrollbackLines]}
|
||||||
|
min={1000}
|
||||||
|
max={100000}
|
||||||
|
step={1000}
|
||||||
|
onValueChange={([value]) => setTerminalScrollbackLines(value)}
|
||||||
|
onValueCommit={() => {
|
||||||
|
toast.info('Scrollback changed', {
|
||||||
|
description: 'Restart terminal for changes to take effect',
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Line Height */}
|
{/* Line Height */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label className="text-sm">Line Height</Label>
|
<Label className="text-xs font-medium">Line Height</Label>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{terminalState.lineHeight.toFixed(1)}
|
{terminalState.lineHeight.toFixed(1)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -1536,18 +1589,21 @@ export function TerminalView() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Default Run Script */}
|
{/* Screen Reader */}
|
||||||
<div className="space-y-2">
|
<div className="flex items-center justify-between">
|
||||||
<Label className="text-sm">Default Run Script</Label>
|
<div className="space-y-0.5">
|
||||||
<Input
|
<Label className="text-xs font-medium">Screen Reader</Label>
|
||||||
value={terminalState.defaultRunScript}
|
<p className="text-[10px] text-muted-foreground">Enable accessibility mode</p>
|
||||||
onChange={(e) => setTerminalDefaultRunScript(e.target.value)}
|
</div>
|
||||||
placeholder="e.g., claude, npm run dev"
|
<Switch
|
||||||
className="h-8 text-sm"
|
checked={terminalState.screenReaderMode}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
setTerminalScreenReaderMode(checked);
|
||||||
|
toast.info(checked ? 'Screen reader enabled' : 'Screen reader disabled', {
|
||||||
|
description: 'Restart terminal for changes to take effect',
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
Command to run when opening new terminals
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
|
|||||||
@@ -30,6 +30,13 @@ import { Slider } from '@/components/ui/slider';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@/components/ui/select';
|
||||||
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
||||||
import { useAppStore, DEFAULT_KEYBOARD_SHORTCUTS, type KeyboardShortcuts } from '@/store/app-store';
|
import { useAppStore, DEFAULT_KEYBOARD_SHORTCUTS, type KeyboardShortcuts } from '@/store/app-store';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
@@ -38,7 +45,9 @@ import {
|
|||||||
getTerminalTheme,
|
getTerminalTheme,
|
||||||
TERMINAL_FONT_OPTIONS,
|
TERMINAL_FONT_OPTIONS,
|
||||||
DEFAULT_TERMINAL_FONT,
|
DEFAULT_TERMINAL_FONT,
|
||||||
|
getTerminalFontFamily,
|
||||||
} from '@/config/terminal-themes';
|
} from '@/config/terminal-themes';
|
||||||
|
import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client';
|
import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client';
|
||||||
@@ -567,7 +576,7 @@ export function TerminalPanel({
|
|||||||
// Get settings from store (read at initialization time)
|
// Get settings from store (read at initialization time)
|
||||||
const terminalSettings = useAppStore.getState().terminalState;
|
const terminalSettings = useAppStore.getState().terminalState;
|
||||||
const screenReaderEnabled = terminalSettings.screenReaderMode;
|
const screenReaderEnabled = terminalSettings.screenReaderMode;
|
||||||
const terminalFontFamily = terminalSettings.fontFamily || DEFAULT_TERMINAL_FONT;
|
const terminalFontFamily = getTerminalFontFamily(terminalSettings.fontFamily);
|
||||||
const terminalScrollback = terminalSettings.scrollbackLines || 5000;
|
const terminalScrollback = terminalSettings.scrollbackLines || 5000;
|
||||||
const terminalLineHeight = terminalSettings.lineHeight || 1.0;
|
const terminalLineHeight = terminalSettings.lineHeight || 1.0;
|
||||||
|
|
||||||
@@ -1269,7 +1278,7 @@ export function TerminalPanel({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (xtermRef.current && isTerminalReady) {
|
if (xtermRef.current && isTerminalReady) {
|
||||||
xtermRef.current.options.fontFamily = fontFamily;
|
xtermRef.current.options.fontFamily = getTerminalFontFamily(fontFamily);
|
||||||
fitAddonRef.current?.fit();
|
fitAddonRef.current?.fit();
|
||||||
}
|
}
|
||||||
}, [fontFamily, isTerminalReady]);
|
}, [fontFamily, isTerminalReady]);
|
||||||
@@ -1902,22 +1911,33 @@ export function TerminalPanel({
|
|||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs font-medium">Font Family</Label>
|
<Label className="text-xs font-medium">Font Family</Label>
|
||||||
<select
|
<Select
|
||||||
value={fontFamily}
|
value={fontFamily || DEFAULT_FONT_VALUE}
|
||||||
onChange={(e) => {
|
onValueChange={(value) => {
|
||||||
setTerminalFontFamily(e.target.value);
|
setTerminalFontFamily(value);
|
||||||
toast.info('Font family changed', {
|
toast.info('Font family changed', {
|
||||||
description: 'Restart terminal for changes to take effect',
|
description: 'Restart terminal for changes to take effect',
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="w-full h-7 text-xs bg-background border border-input rounded-md px-2"
|
|
||||||
>
|
>
|
||||||
{TERMINAL_FONT_OPTIONS.map((font) => (
|
<SelectTrigger className="w-full h-8 text-xs">
|
||||||
<option key={font.value} value={font.value}>
|
<SelectValue placeholder="Default (Menlo / Monaco)" />
|
||||||
{font.label}
|
</SelectTrigger>
|
||||||
</option>
|
<SelectContent>
|
||||||
))}
|
{TERMINAL_FONT_OPTIONS.map((option) => (
|
||||||
</select>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontFamily:
|
||||||
|
option.value === DEFAULT_FONT_VALUE ? undefined : option.value,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -4,6 +4,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ThemeMode } from '@/store/app-store';
|
import type { ThemeMode } from '@/store/app-store';
|
||||||
|
import {
|
||||||
|
UI_MONO_FONT_OPTIONS,
|
||||||
|
DEFAULT_FONT_VALUE,
|
||||||
|
type UIFontOption,
|
||||||
|
} from '@/config/ui-font-options';
|
||||||
|
|
||||||
export interface TerminalTheme {
|
export interface TerminalTheme {
|
||||||
background: string;
|
background: string;
|
||||||
@@ -37,27 +42,44 @@ export interface TerminalTheme {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminal font options for user selection
|
* Terminal font options for user selection
|
||||||
* These are monospace fonts commonly available on different platforms
|
*
|
||||||
|
* Uses the same fonts as UI_MONO_FONT_OPTIONS for consistency across the app.
|
||||||
|
* All fonts listed here are bundled with the app via @fontsource packages
|
||||||
|
* or are system fonts with appropriate fallbacks.
|
||||||
*/
|
*/
|
||||||
export interface TerminalFontOption {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TERMINAL_FONT_OPTIONS: TerminalFontOption[] = [
|
// Re-export for backwards compatibility
|
||||||
{ value: "Menlo, Monaco, 'Courier New', monospace", label: 'Menlo / Monaco' },
|
export type TerminalFontOption = UIFontOption;
|
||||||
{ value: "'SF Mono', Menlo, Monaco, monospace", label: 'SF Mono' },
|
|
||||||
{ value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' },
|
|
||||||
{ value: "'Fira Code', monospace", label: 'Fira Code' },
|
|
||||||
{ value: "'Source Code Pro', monospace", label: 'Source Code Pro' },
|
|
||||||
{ value: "Consolas, 'Courier New', monospace", label: 'Consolas' },
|
|
||||||
{ value: "'Ubuntu Mono', monospace", label: 'Ubuntu Mono' },
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default terminal font family (first option)
|
* Terminal font options - reuses UI_MONO_FONT_OPTIONS with terminal-specific default
|
||||||
|
*
|
||||||
|
* The 'default' value means "use the default terminal font" (Menlo/Monaco)
|
||||||
*/
|
*/
|
||||||
export const DEFAULT_TERMINAL_FONT = TERMINAL_FONT_OPTIONS[0].value;
|
export const TERMINAL_FONT_OPTIONS: readonly UIFontOption[] = UI_MONO_FONT_OPTIONS.map((option) => {
|
||||||
|
// Replace the UI default label with terminal-specific default
|
||||||
|
if (option.value === DEFAULT_FONT_VALUE) {
|
||||||
|
return { value: option.value, label: 'Default (Menlo / Monaco)' };
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default terminal font family
|
||||||
|
* Uses the DEFAULT_FONT_VALUE sentinel which maps to Menlo/Monaco
|
||||||
|
*/
|
||||||
|
export const DEFAULT_TERMINAL_FONT = DEFAULT_FONT_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the actual font family CSS value for terminal
|
||||||
|
* Converts DEFAULT_FONT_VALUE to the actual Menlo/Monaco font stack
|
||||||
|
*/
|
||||||
|
export function getTerminalFontFamily(fontValue: string | undefined): string {
|
||||||
|
if (!fontValue || fontValue === DEFAULT_FONT_VALUE) {
|
||||||
|
return "Menlo, Monaco, 'Courier New', monospace";
|
||||||
|
}
|
||||||
|
return fontValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Dark theme (default)
|
// Dark theme (default)
|
||||||
const darkTheme: TerminalTheme = {
|
const darkTheme: TerminalTheme = {
|
||||||
|
|||||||
@@ -600,6 +600,13 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
|
|||||||
worktreePanelCollapsed: settings.worktreePanelCollapsed ?? false,
|
worktreePanelCollapsed: settings.worktreePanelCollapsed ?? false,
|
||||||
lastProjectDir: settings.lastProjectDir ?? '',
|
lastProjectDir: settings.lastProjectDir ?? '',
|
||||||
recentFolders: settings.recentFolders ?? [],
|
recentFolders: settings.recentFolders ?? [],
|
||||||
|
// Terminal font (nested in terminalState)
|
||||||
|
...(settings.terminalFontFamily && {
|
||||||
|
terminalState: {
|
||||||
|
...current.terminalState,
|
||||||
|
fontFamily: settings.terminalFontFamily,
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hydrate setup wizard state from global settings (API-backed)
|
// Hydrate setup wizard state from global settings (API-backed)
|
||||||
@@ -653,6 +660,7 @@ function buildSettingsUpdateFromStore(): Record<string, unknown> {
|
|||||||
worktreePanelCollapsed: state.worktreePanelCollapsed,
|
worktreePanelCollapsed: state.worktreePanelCollapsed,
|
||||||
lastProjectDir: state.lastProjectDir,
|
lastProjectDir: state.lastProjectDir,
|
||||||
recentFolders: state.recentFolders,
|
recentFolders: state.recentFolders,
|
||||||
|
terminalFontFamily: state.terminalState.fontFamily,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'theme',
|
'theme',
|
||||||
'fontFamilySans',
|
'fontFamilySans',
|
||||||
'fontFamilyMono',
|
'fontFamilyMono',
|
||||||
|
'terminalFontFamily', // Maps to terminalState.fontFamily
|
||||||
'sidebarOpen',
|
'sidebarOpen',
|
||||||
'chatHistoryOpen',
|
'chatHistoryOpen',
|
||||||
'maxConcurrency',
|
'maxConcurrency',
|
||||||
@@ -159,6 +160,9 @@ export function useSettingsSync(): SettingsSyncState {
|
|||||||
if (field === 'currentProjectId') {
|
if (field === 'currentProjectId') {
|
||||||
// Special handling: extract ID from currentProject object
|
// Special handling: extract ID from currentProject object
|
||||||
updates[field] = appState.currentProject?.id ?? null;
|
updates[field] = appState.currentProject?.id ?? null;
|
||||||
|
} else if (field === 'terminalFontFamily') {
|
||||||
|
// Special handling: map terminalState.fontFamily to terminalFontFamily
|
||||||
|
updates[field] = appState.terminalState.fontFamily;
|
||||||
} else {
|
} else {
|
||||||
updates[field] = appState[field as keyof typeof appState];
|
updates[field] = appState[field as keyof typeof appState];
|
||||||
}
|
}
|
||||||
@@ -260,6 +264,8 @@ export function useSettingsSync(): SettingsSyncState {
|
|||||||
for (const field of SETTINGS_FIELDS_TO_SYNC) {
|
for (const field of SETTINGS_FIELDS_TO_SYNC) {
|
||||||
if (field === 'currentProjectId') {
|
if (field === 'currentProjectId') {
|
||||||
updates[field] = appState.currentProject?.id ?? null;
|
updates[field] = appState.currentProject?.id ?? null;
|
||||||
|
} else if (field === 'terminalFontFamily') {
|
||||||
|
updates[field] = appState.terminalState.fontFamily;
|
||||||
} else {
|
} else {
|
||||||
updates[field] = appState[field as keyof typeof appState];
|
updates[field] = appState[field as keyof typeof appState];
|
||||||
}
|
}
|
||||||
@@ -322,6 +328,12 @@ export function useSettingsSync(): SettingsSyncState {
|
|||||||
changed = true;
|
changed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (field === 'terminalFontFamily') {
|
||||||
|
// Special handling: compare terminalState.fontFamily
|
||||||
|
if (newState.terminalState.fontFamily !== prevState.terminalState.fontFamily) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const key = field as keyof typeof newState;
|
const key = field as keyof typeof newState;
|
||||||
if (newState[key] !== prevState[key]) {
|
if (newState[key] !== prevState[key]) {
|
||||||
@@ -403,6 +415,8 @@ export async function forceSyncSettingsToServer(): Promise<boolean> {
|
|||||||
for (const field of SETTINGS_FIELDS_TO_SYNC) {
|
for (const field of SETTINGS_FIELDS_TO_SYNC) {
|
||||||
if (field === 'currentProjectId') {
|
if (field === 'currentProjectId') {
|
||||||
updates[field] = appState.currentProject?.id ?? null;
|
updates[field] = appState.currentProject?.id ?? null;
|
||||||
|
} else if (field === 'terminalFontFamily') {
|
||||||
|
updates[field] = appState.terminalState.fontFamily;
|
||||||
} else {
|
} else {
|
||||||
updates[field] = appState[field as keyof typeof appState];
|
updates[field] = appState[field as keyof typeof appState];
|
||||||
}
|
}
|
||||||
@@ -505,6 +519,13 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
|
|||||||
worktreePanelCollapsed: serverSettings.worktreePanelCollapsed ?? false,
|
worktreePanelCollapsed: serverSettings.worktreePanelCollapsed ?? false,
|
||||||
lastProjectDir: serverSettings.lastProjectDir ?? '',
|
lastProjectDir: serverSettings.lastProjectDir ?? '',
|
||||||
recentFolders: serverSettings.recentFolders ?? [],
|
recentFolders: serverSettings.recentFolders ?? [],
|
||||||
|
// Terminal font (nested in terminalState)
|
||||||
|
...(serverSettings.terminalFontFamily && {
|
||||||
|
terminalState: {
|
||||||
|
...currentAppState.terminalState,
|
||||||
|
fontFamily: serverSettings.terminalFontFamily,
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Also refresh setup wizard state
|
// Also refresh setup wizard state
|
||||||
|
|||||||
@@ -880,6 +880,11 @@
|
|||||||
background: var(--muted-foreground);
|
background: var(--muted-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Terminal padding for better readability */
|
||||||
|
.xterm {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
DEPENDENCY GRAPH STYLES
|
DEPENDENCY GRAPH STYLES
|
||||||
Theme-aware styling for React Flow graph
|
Theme-aware styling for React Flow graph
|
||||||
|
|||||||
@@ -472,6 +472,8 @@ export interface GlobalSettings {
|
|||||||
fontFamilySans?: string;
|
fontFamilySans?: string;
|
||||||
/** Global Code/Mono font family (undefined = use default Geist Mono) */
|
/** Global Code/Mono font family (undefined = use default Geist Mono) */
|
||||||
fontFamilyMono?: string;
|
fontFamilyMono?: string;
|
||||||
|
/** Terminal font family (undefined = use default Menlo/Monaco) */
|
||||||
|
terminalFontFamily?: string;
|
||||||
|
|
||||||
// UI State Preferences
|
// UI State Preferences
|
||||||
/** Whether sidebar is currently open */
|
/** Whether sidebar is currently open */
|
||||||
|
|||||||
Reference in New Issue
Block a user