feat: add selectable interface and code fonts (10 for each)
Adds separate UI and code font choices in settings Applies font changes immediately Lazy-loads selected remote fonts
This commit is contained in:
@@ -43,6 +43,7 @@ import { useSync } from '@/sync/use-sync';
|
|||||||
import { setOptimisticRefs } from '@/sync/session-actions';
|
import { setOptimisticRefs } from '@/sync/session-actions';
|
||||||
import { useFontPreferences } from '@/hooks/useFontPreferences';
|
import { useFontPreferences } from '@/hooks/useFontPreferences';
|
||||||
import { CODE_FONT_OPTION_MAP, DEFAULT_MONO_FONT, DEFAULT_UI_FONT, UI_FONT_OPTION_MAP } from '@/lib/fontOptions';
|
import { CODE_FONT_OPTION_MAP, DEFAULT_MONO_FONT, DEFAULT_UI_FONT, UI_FONT_OPTION_MAP } from '@/lib/fontOptions';
|
||||||
|
import { loadMonoFont, loadUiFont } from '@/lib/fontLoader';
|
||||||
import { ConfigUpdateOverlay } from '@/components/ui/ConfigUpdateOverlay';
|
import { ConfigUpdateOverlay } from '@/components/ui/ConfigUpdateOverlay';
|
||||||
import { AboutDialog } from '@/components/ui/AboutDialog';
|
import { AboutDialog } from '@/components/ui/AboutDialog';
|
||||||
import { RuntimeAPIProvider } from '@/contexts/RuntimeAPIProvider';
|
import { RuntimeAPIProvider } from '@/contexts/RuntimeAPIProvider';
|
||||||
@@ -269,6 +270,8 @@ function App({ apis }: AppProps) {
|
|||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const uiStack = UI_FONT_OPTION_MAP[uiFont]?.stack ?? UI_FONT_OPTION_MAP[DEFAULT_UI_FONT].stack;
|
const uiStack = UI_FONT_OPTION_MAP[uiFont]?.stack ?? UI_FONT_OPTION_MAP[DEFAULT_UI_FONT].stack;
|
||||||
const monoStack = CODE_FONT_OPTION_MAP[monoFont]?.stack ?? CODE_FONT_OPTION_MAP[DEFAULT_MONO_FONT].stack;
|
const monoStack = CODE_FONT_OPTION_MAP[monoFont]?.stack ?? CODE_FONT_OPTION_MAP[DEFAULT_MONO_FONT].stack;
|
||||||
|
void loadUiFont(uiFont);
|
||||||
|
void loadMonoFont(monoFont);
|
||||||
|
|
||||||
root.style.setProperty('--font-sans', uiStack);
|
root.style.setProperty('--font-sans', uiStack);
|
||||||
root.style.setProperty('--font-heading', uiStack);
|
root.style.setProperty('--font-heading', uiStack);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
|
|||||||
import { useDeviceInfo } from '@/lib/device';
|
import { useDeviceInfo } from '@/lib/device';
|
||||||
import { usePwaDetection } from '@/hooks/usePwaDetection';
|
import { usePwaDetection } from '@/hooks/usePwaDetection';
|
||||||
import { updateDesktopSettings } from '@/lib/persistence';
|
import { updateDesktopSettings } from '@/lib/persistence';
|
||||||
|
import { CODE_FONT_OPTIONS, DEFAULT_MONO_FONT, DEFAULT_UI_FONT, UI_FONT_OPTIONS, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions';
|
||||||
import { useI18n, type Locale } from '@/lib/i18n';
|
import { useI18n, type Locale } from '@/lib/i18n';
|
||||||
import { useConfigStore } from '@/stores/useConfigStore';
|
import { useConfigStore } from '@/stores/useConfigStore';
|
||||||
import {
|
import {
|
||||||
@@ -249,6 +250,10 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
const setFontSize = useUIStore(state => state.setFontSize);
|
const setFontSize = useUIStore(state => state.setFontSize);
|
||||||
const terminalFontSize = useUIStore(state => state.terminalFontSize);
|
const terminalFontSize = useUIStore(state => state.terminalFontSize);
|
||||||
const setTerminalFontSize = useUIStore(state => state.setTerminalFontSize);
|
const setTerminalFontSize = useUIStore(state => state.setTerminalFontSize);
|
||||||
|
const uiFont = useUIStore(state => state.uiFont);
|
||||||
|
const setUiFont = useUIStore(state => state.setUiFont);
|
||||||
|
const monoFont = useUIStore(state => state.monoFont);
|
||||||
|
const setMonoFont = useUIStore(state => state.setMonoFont);
|
||||||
const padding = useUIStore(state => state.padding);
|
const padding = useUIStore(state => state.padding);
|
||||||
const setPadding = useUIStore(state => state.setPadding);
|
const setPadding = useUIStore(state => state.setPadding);
|
||||||
const inputBarOffset = useUIStore(state => state.inputBarOffset);
|
const inputBarOffset = useUIStore(state => state.inputBarOffset);
|
||||||
@@ -847,6 +852,72 @@ export const OpenChamberVisualSettings: React.FC<OpenChamberVisualSettingsProps>
|
|||||||
<h4 className="typography-ui-header font-medium text-foreground">{t('settings.openchamber.visual.section.spacingAndLayout')}</h4>
|
<h4 className="typography-ui-header font-medium text-foreground">{t('settings.openchamber.visual.section.spacingAndLayout')}</h4>
|
||||||
<div className="pl-2">
|
<div className="pl-2">
|
||||||
|
|
||||||
|
{shouldShow('fontSize') && !isMobile && (
|
||||||
|
<div className="flex items-center gap-8 py-1">
|
||||||
|
<div className="flex min-w-0 flex-col w-56 shrink-0">
|
||||||
|
<span className="typography-ui-label text-foreground">{t('settings.openchamber.visual.field.interfaceFont')}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 w-fit">
|
||||||
|
<Select value={uiFont} onValueChange={(value) => setUiFont(value as UiFontOption)}>
|
||||||
|
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectInterfaceFontAria')} className="w-[13rem]">
|
||||||
|
<SelectValue>{UI_FONT_OPTIONS.find((option) => option.id === uiFont)?.label}</SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{UI_FONT_OPTIONS.map((option) => (
|
||||||
|
<SelectItem key={option.id} value={option.id}>
|
||||||
|
<span style={{ fontFamily: option.stack }}>{option.label}</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Button size="sm"
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setUiFont(DEFAULT_UI_FONT)}
|
||||||
|
disabled={uiFont === DEFAULT_UI_FONT}
|
||||||
|
className="h-7 w-7 px-0 text-muted-foreground hover:text-foreground"
|
||||||
|
aria-label={t('settings.openchamber.visual.actions.resetInterfaceFontAria')}
|
||||||
|
title={t('settings.common.actions.reset')}
|
||||||
|
>
|
||||||
|
<RiRestartLine className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shouldShow('terminalFontSize') && (
|
||||||
|
<div className={cn("py-1", isMobile ? "flex flex-col gap-3" : "flex items-center gap-8")}>
|
||||||
|
<div className={cn("flex min-w-0 flex-col", isMobile ? "w-full" : "w-56 shrink-0")}>
|
||||||
|
<span className="typography-ui-label text-foreground">{t('settings.openchamber.visual.field.codeFont')}</span>
|
||||||
|
</div>
|
||||||
|
<div className={cn("flex items-center gap-2", isMobile ? "w-full" : "w-fit")}>
|
||||||
|
<Select value={monoFont} onValueChange={(value) => setMonoFont(value as MonoFontOption)}>
|
||||||
|
<SelectTrigger aria-label={t('settings.openchamber.visual.field.selectCodeFontAria')} className="w-[13rem]">
|
||||||
|
<SelectValue>{CODE_FONT_OPTIONS.find((option) => option.id === monoFont)?.label}</SelectValue>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{CODE_FONT_OPTIONS.map((option) => (
|
||||||
|
<SelectItem key={option.id} value={option.id}>
|
||||||
|
<span style={{ fontFamily: option.stack }}>{option.label}</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Button size="sm"
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setMonoFont(DEFAULT_MONO_FONT)}
|
||||||
|
disabled={monoFont === DEFAULT_MONO_FONT}
|
||||||
|
className="h-7 w-7 px-0 text-muted-foreground hover:text-foreground"
|
||||||
|
aria-label={t('settings.openchamber.visual.actions.resetCodeFontAria')}
|
||||||
|
title={t('settings.common.actions.reset')}
|
||||||
|
>
|
||||||
|
<RiRestartLine className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{shouldShow('fontSize') && !isMobile && (
|
{shouldShow('fontSize') && !isMobile && (
|
||||||
<div className="flex items-center gap-8 py-1">
|
<div className="flex items-center gap-8 py-1">
|
||||||
<div className="flex min-w-0 flex-col w-56 shrink-0">
|
<div className="flex min-w-0 flex-col w-56 shrink-0">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { MonoFontOption, UiFontOption } from '@/lib/fontOptions';
|
import type { MonoFontOption, UiFontOption } from '@/lib/fontOptions';
|
||||||
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
|
||||||
interface FontPreferences {
|
interface FontPreferences {
|
||||||
uiFont: UiFontOption;
|
uiFont: UiFontOption;
|
||||||
@@ -6,8 +7,11 @@ interface FontPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useFontPreferences = (): FontPreferences => {
|
export const useFontPreferences = (): FontPreferences => {
|
||||||
|
const uiFont = useUIStore(state => state.uiFont);
|
||||||
|
const monoFont = useUIStore(state => state.monoFont);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uiFont: 'ibm-plex-sans',
|
uiFont,
|
||||||
monoFont: 'ibm-plex-mono',
|
monoFont,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -574,6 +574,8 @@ export interface SettingsPayload {
|
|||||||
mermaidRenderingMode?: 'svg' | 'ascii';
|
mermaidRenderingMode?: 'svg' | 'ascii';
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
terminalFontSize?: number;
|
terminalFontSize?: number;
|
||||||
|
uiFont?: string;
|
||||||
|
monoFont?: string;
|
||||||
padding?: number;
|
padding?: number;
|
||||||
cornerRadius?: number;
|
cornerRadius?: number;
|
||||||
inputBarOffset?: number;
|
inputBarOffset?: number;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { updateDesktopSettings } from '@/lib/persistence';
|
import { updateDesktopSettings } from '@/lib/persistence';
|
||||||
import type { DesktopSettings } from '@/lib/desktop';
|
import type { DesktopSettings } from '@/lib/desktop';
|
||||||
|
import type { MonoFontOption, UiFontOption } from '@/lib/fontOptions';
|
||||||
|
|
||||||
type AppearanceSlice = {
|
type AppearanceSlice = {
|
||||||
showReasoningTraces: boolean;
|
showReasoningTraces: boolean;
|
||||||
@@ -26,6 +27,8 @@ type AppearanceSlice = {
|
|||||||
sessionRetentionAction: 'archive' | 'delete';
|
sessionRetentionAction: 'archive' | 'delete';
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
terminalFontSize: number;
|
terminalFontSize: number;
|
||||||
|
uiFont: UiFontOption;
|
||||||
|
monoFont: MonoFontOption;
|
||||||
padding: number;
|
padding: number;
|
||||||
cornerRadius: number;
|
cornerRadius: number;
|
||||||
inputBarOffset: number;
|
inputBarOffset: number;
|
||||||
@@ -62,6 +65,8 @@ export const startAppearanceAutoSave = (): void => {
|
|||||||
sessionRetentionAction: useUIStore.getState().sessionRetentionAction,
|
sessionRetentionAction: useUIStore.getState().sessionRetentionAction,
|
||||||
fontSize: useUIStore.getState().fontSize,
|
fontSize: useUIStore.getState().fontSize,
|
||||||
terminalFontSize: useUIStore.getState().terminalFontSize,
|
terminalFontSize: useUIStore.getState().terminalFontSize,
|
||||||
|
uiFont: useUIStore.getState().uiFont,
|
||||||
|
monoFont: useUIStore.getState().monoFont,
|
||||||
padding: useUIStore.getState().padding,
|
padding: useUIStore.getState().padding,
|
||||||
cornerRadius: useUIStore.getState().cornerRadius,
|
cornerRadius: useUIStore.getState().cornerRadius,
|
||||||
inputBarOffset: useUIStore.getState().inputBarOffset,
|
inputBarOffset: useUIStore.getState().inputBarOffset,
|
||||||
@@ -110,6 +115,8 @@ export const startAppearanceAutoSave = (): void => {
|
|||||||
sessionRetentionAction: state.sessionRetentionAction,
|
sessionRetentionAction: state.sessionRetentionAction,
|
||||||
fontSize: state.fontSize,
|
fontSize: state.fontSize,
|
||||||
terminalFontSize: state.terminalFontSize,
|
terminalFontSize: state.terminalFontSize,
|
||||||
|
uiFont: state.uiFont,
|
||||||
|
monoFont: state.monoFont,
|
||||||
padding: state.padding,
|
padding: state.padding,
|
||||||
cornerRadius: state.cornerRadius,
|
cornerRadius: state.cornerRadius,
|
||||||
inputBarOffset: state.inputBarOffset,
|
inputBarOffset: state.inputBarOffset,
|
||||||
@@ -174,6 +181,12 @@ export const startAppearanceAutoSave = (): void => {
|
|||||||
if (current.terminalFontSize !== previous.terminalFontSize) {
|
if (current.terminalFontSize !== previous.terminalFontSize) {
|
||||||
diff.terminalFontSize = current.terminalFontSize;
|
diff.terminalFontSize = current.terminalFontSize;
|
||||||
}
|
}
|
||||||
|
if (current.uiFont !== previous.uiFont) {
|
||||||
|
diff.uiFont = current.uiFont;
|
||||||
|
}
|
||||||
|
if (current.monoFont !== previous.monoFont) {
|
||||||
|
diff.monoFont = current.monoFont;
|
||||||
|
}
|
||||||
if (current.padding !== previous.padding) {
|
if (current.padding !== previous.padding) {
|
||||||
diff.padding = current.padding;
|
diff.padding = current.padding;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ export type DesktopSettings = {
|
|||||||
stickyUserHeader?: boolean;
|
stickyUserHeader?: boolean;
|
||||||
fontSize?: number;
|
fontSize?: number;
|
||||||
terminalFontSize?: number;
|
terminalFontSize?: number;
|
||||||
|
uiFont?: string;
|
||||||
|
monoFont?: string;
|
||||||
padding?: number;
|
padding?: number;
|
||||||
cornerRadius?: number;
|
cornerRadius?: number;
|
||||||
inputBarOffset?: number;
|
inputBarOffset?: number;
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { CODE_FONT_OPTION_MAP, UI_FONT_OPTION_MAP, type FontFaceSource, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions';
|
||||||
|
|
||||||
|
const loadedFaces = new Set<string>();
|
||||||
|
const pendingFaces = new Map<string, Promise<void>>();
|
||||||
|
|
||||||
|
const buildFontUrl = (source: FontFaceSource, weight: number) => {
|
||||||
|
const packageName = encodeURIComponent(source.packageName).replace('%40', '@').replace('%2F', '/');
|
||||||
|
return `https://cdn.jsdelivr.net/npm/${packageName}/files/${source.filePrefix}-latin-${weight}-normal.woff2`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadFace = (source: FontFaceSource, weight: number) => {
|
||||||
|
const key = `${source.family}:${weight}`;
|
||||||
|
if (loadedFaces.has(key)) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pending = pendingFaces.get(key);
|
||||||
|
if (pending) {
|
||||||
|
return pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof document === 'undefined' || typeof FontFace === 'undefined' || !document.fonts) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const face = new FontFace(source.family, `url(${buildFontUrl(source, weight)}) format('woff2')`, {
|
||||||
|
style: 'normal',
|
||||||
|
weight: String(weight),
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
|
|
||||||
|
document.fonts.add(face);
|
||||||
|
const promise = face.load()
|
||||||
|
.then(() => {
|
||||||
|
loadedFaces.add(key);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
document.fonts.delete(face);
|
||||||
|
console.warn(`Failed to load font: ${source.family} ${weight}`, error);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
pendingFaces.delete(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
pendingFaces.set(key, promise);
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadSource = (source: FontFaceSource | undefined) => {
|
||||||
|
if (!source) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(source.weights.map((weight) => loadFace(source, weight))).then(() => undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadUiFont = (font: UiFontOption) => loadSource(UI_FONT_OPTION_MAP[font]?.source);
|
||||||
|
|
||||||
|
export const loadMonoFont = (font: MonoFontOption) => loadSource(CODE_FONT_OPTION_MAP[font]?.source);
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
export type UiFontOption = 'ibm-plex-sans';
|
export type UiFontOption = 'ibm-plex-sans' | 'inter' | 'geist-sans' | 'atkinson-hyperlegible' | 'source-sans-3' | 'roboto' | 'noto-sans' | 'dm-sans' | 'manrope' | 'system';
|
||||||
|
|
||||||
export type MonoFontOption = 'ibm-plex-mono';
|
export type MonoFontOption = 'ibm-plex-mono' | 'jetbrains-mono' | 'fira-code' | 'geist-mono' | 'commit-mono' | 'source-code-pro' | 'cascadia-code' | 'roboto-mono' | 'iosevka' | 'system-mono';
|
||||||
|
|
||||||
|
export interface FontFaceSource {
|
||||||
|
family: string;
|
||||||
|
packageName: string;
|
||||||
|
filePrefix: string;
|
||||||
|
weights: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface FontOptionDefinition<T extends string> {
|
export interface FontOptionDefinition<T extends string> {
|
||||||
id: T;
|
id: T;
|
||||||
@@ -8,6 +15,7 @@ export interface FontOptionDefinition<T extends string> {
|
|||||||
description: string;
|
description: string;
|
||||||
stack: string;
|
stack: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
|
source?: FontFaceSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UI_FONT_OPTIONS: FontOptionDefinition<UiFontOption>[] = [
|
export const UI_FONT_OPTIONS: FontOptionDefinition<UiFontOption>[] = [
|
||||||
@@ -16,6 +24,68 @@ export const UI_FONT_OPTIONS: FontOptionDefinition<UiFontOption>[] = [
|
|||||||
label: 'IBM Plex Sans',
|
label: 'IBM Plex Sans',
|
||||||
description: 'Humanist sans-serif for optimal readability in the interface.',
|
description: 'Humanist sans-serif for optimal readability in the interface.',
|
||||||
stack: '"IBM Plex Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
stack: '"IBM Plex Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'inter',
|
||||||
|
label: 'Inter',
|
||||||
|
description: 'Modern UI sans with excellent readability at small sizes.',
|
||||||
|
stack: '"Inter", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Inter', packageName: '@fontsource/inter', filePrefix: 'inter', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'geist-sans',
|
||||||
|
label: 'Geist Sans',
|
||||||
|
description: 'Crisp sans-serif with a technical interface feel.',
|
||||||
|
stack: '"Geist Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Geist Sans', packageName: '@fontsource/geist-sans', filePrefix: 'geist-sans', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'atkinson-hyperlegible',
|
||||||
|
label: 'Atkinson Hyperlegible',
|
||||||
|
description: 'Accessibility-focused sans-serif optimized for character distinction.',
|
||||||
|
stack: '"Atkinson Hyperlegible", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Atkinson Hyperlegible', packageName: '@fontsource/atkinson-hyperlegible', filePrefix: 'atkinson-hyperlegible', weights: [400, 700] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'source-sans-3',
|
||||||
|
label: 'Source Sans 3',
|
||||||
|
description: 'Adobe sans-serif tuned for clean, readable interfaces.',
|
||||||
|
stack: '"Source Sans 3", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Source Sans 3', packageName: '@fontsource/source-sans-3', filePrefix: 'source-sans-3', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'roboto',
|
||||||
|
label: 'Roboto',
|
||||||
|
description: 'Familiar Material-style sans-serif with broad UI usage.',
|
||||||
|
stack: '"Roboto", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Roboto', packageName: '@fontsource/roboto', filePrefix: 'roboto', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'noto-sans',
|
||||||
|
label: 'Noto Sans',
|
||||||
|
description: 'Readable sans-serif with strong international coverage.',
|
||||||
|
stack: '"Noto Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Noto Sans', packageName: '@fontsource/noto-sans', filePrefix: 'noto-sans', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'dm-sans',
|
||||||
|
label: 'DM Sans',
|
||||||
|
description: 'Modern product UI sans-serif with friendly proportions.',
|
||||||
|
stack: '"DM Sans", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'DM Sans', packageName: '@fontsource/dm-sans', filePrefix: 'dm-sans', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'manrope',
|
||||||
|
label: 'Manrope',
|
||||||
|
description: 'Polished geometric sans-serif for modern app interfaces.',
|
||||||
|
stack: '"Manrope", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||||
|
source: { family: 'Manrope', packageName: '@fontsource/manrope', filePrefix: 'manrope', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'system',
|
||||||
|
label: 'System',
|
||||||
|
description: 'Native operating system interface font.',
|
||||||
|
stack: '"SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -25,6 +95,68 @@ export const CODE_FONT_OPTIONS: FontOptionDefinition<MonoFontOption>[] = [
|
|||||||
label: 'IBM Plex Mono',
|
label: 'IBM Plex Mono',
|
||||||
description: 'Balanced monospace for code blocks and technical content.',
|
description: 'Balanced monospace for code blocks and technical content.',
|
||||||
stack: '"IBM Plex Mono", "SFMono-Regular", "Menlo", monospace'
|
stack: '"IBM Plex Mono", "SFMono-Regular", "Menlo", monospace'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'jetbrains-mono',
|
||||||
|
label: 'JetBrains Mono',
|
||||||
|
description: 'Developer-focused monospace with strong punctuation clarity.',
|
||||||
|
stack: '"JetBrains Mono", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'JetBrains Mono', packageName: '@fontsource/jetbrains-mono', filePrefix: 'jetbrains-mono', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fira-code',
|
||||||
|
label: 'Fira Code',
|
||||||
|
description: 'Readable coding font with ligature support.',
|
||||||
|
stack: '"Fira Code", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Fira Code', packageName: '@fontsource/fira-code', filePrefix: 'fira-code', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'geist-mono',
|
||||||
|
label: 'Geist Mono',
|
||||||
|
description: 'Sharp monospace pair for Geist Sans.',
|
||||||
|
stack: '"Geist Mono", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Geist Mono', packageName: '@fontsource/geist-mono', filePrefix: 'geist-mono', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'commit-mono',
|
||||||
|
label: 'Commit Mono',
|
||||||
|
description: 'Code-oriented monospace with polished editor ergonomics.',
|
||||||
|
stack: '"Commit Mono", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Commit Mono', packageName: '@fontsource/commit-mono', filePrefix: 'commit-mono', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'source-code-pro',
|
||||||
|
label: 'Source Code Pro',
|
||||||
|
description: 'Adobe monospace designed for source code readability.',
|
||||||
|
stack: '"Source Code Pro", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Source Code Pro', packageName: '@fontsource/source-code-pro', filePrefix: 'source-code-pro', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'cascadia-code',
|
||||||
|
label: 'Cascadia Code',
|
||||||
|
description: 'Microsoft coding font popular in terminals and editors.',
|
||||||
|
stack: '"Cascadia Code", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Cascadia Code', packageName: '@fontsource/cascadia-code', filePrefix: 'cascadia-code', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'roboto-mono',
|
||||||
|
label: 'Roboto Mono',
|
||||||
|
description: 'Neutral monospace companion to Roboto.',
|
||||||
|
stack: '"Roboto Mono", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Roboto Mono', packageName: '@fontsource/roboto-mono', filePrefix: 'roboto-mono', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'iosevka',
|
||||||
|
label: 'Iosevka',
|
||||||
|
description: 'Compact monospace for dense code and terminal layouts.',
|
||||||
|
stack: '"Iosevka", "SFMono-Regular", "Menlo", monospace',
|
||||||
|
source: { family: 'Iosevka', packageName: '@fontsource/iosevka', filePrefix: 'iosevka', weights: [400, 500, 600] }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'system-mono',
|
||||||
|
label: 'System Mono',
|
||||||
|
description: 'Native operating system monospace font.',
|
||||||
|
stack: 'ui-monospace, "SFMono-Regular", "Menlo", "Cascadia Mono", "Segoe UI Mono", monospace'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -36,3 +168,9 @@ export const CODE_FONT_OPTION_MAP = buildFontMap(CODE_FONT_OPTIONS);
|
|||||||
|
|
||||||
export const DEFAULT_UI_FONT: UiFontOption = 'ibm-plex-sans';
|
export const DEFAULT_UI_FONT: UiFontOption = 'ibm-plex-sans';
|
||||||
export const DEFAULT_MONO_FONT: MonoFontOption = 'ibm-plex-mono';
|
export const DEFAULT_MONO_FONT: MonoFontOption = 'ibm-plex-mono';
|
||||||
|
|
||||||
|
export const isUiFontOption = (value: unknown): value is UiFontOption =>
|
||||||
|
typeof value === 'string' && value in UI_FONT_OPTION_MAP;
|
||||||
|
|
||||||
|
export const isMonoFontOption = (value: unknown): value is MonoFontOption =>
|
||||||
|
typeof value === 'string' && value in CODE_FONT_OPTION_MAP;
|
||||||
|
|||||||
@@ -1363,9 +1363,15 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.selectOrientationPlaceholder': 'Select orientation',
|
'settings.openchamber.visual.field.selectOrientationPlaceholder': 'Select orientation',
|
||||||
'settings.openchamber.visual.actions.resetInstallOrientationAria': 'Reset install orientation',
|
'settings.openchamber.visual.actions.resetInstallOrientationAria': 'Reset install orientation',
|
||||||
'settings.openchamber.visual.field.interfaceFontSize': 'Interface Font Size',
|
'settings.openchamber.visual.field.interfaceFontSize': 'Interface Font Size',
|
||||||
|
'settings.openchamber.visual.field.interfaceFont': 'Interface Font',
|
||||||
|
'settings.openchamber.visual.field.selectInterfaceFontAria': 'Select interface font',
|
||||||
|
'settings.openchamber.visual.actions.resetInterfaceFontAria': 'Reset interface font',
|
||||||
'settings.openchamber.visual.field.fontSizePercentageAria': 'Font size percentage',
|
'settings.openchamber.visual.field.fontSizePercentageAria': 'Font size percentage',
|
||||||
'settings.openchamber.visual.actions.resetFontSizeAria': 'Reset font size',
|
'settings.openchamber.visual.actions.resetFontSizeAria': 'Reset font size',
|
||||||
'settings.openchamber.visual.field.terminalFontSize': 'Terminal Font Size',
|
'settings.openchamber.visual.field.terminalFontSize': 'Terminal Font Size',
|
||||||
|
'settings.openchamber.visual.field.codeFont': 'Code Font',
|
||||||
|
'settings.openchamber.visual.field.selectCodeFontAria': 'Select code font',
|
||||||
|
'settings.openchamber.visual.actions.resetCodeFontAria': 'Reset code font',
|
||||||
'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'Reset terminal font size',
|
'settings.openchamber.visual.actions.resetTerminalFontSizeAria': 'Reset terminal font size',
|
||||||
'settings.openchamber.visual.field.spacingDensity': 'Spacing Density',
|
'settings.openchamber.visual.field.spacingDensity': 'Spacing Density',
|
||||||
'settings.openchamber.visual.actions.resetSpacingAria': 'Reset spacing',
|
'settings.openchamber.visual.actions.resetSpacingAria': 'Reset spacing',
|
||||||
|
|||||||
@@ -1363,9 +1363,15 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Seleccionar orientación",
|
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Seleccionar orientación",
|
||||||
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Restablecer orientación de instalación",
|
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Restablecer orientación de instalación",
|
||||||
"settings.openchamber.visual.field.interfaceFontSize": "Tamaño de fuente de la interfaz",
|
"settings.openchamber.visual.field.interfaceFontSize": "Tamaño de fuente de la interfaz",
|
||||||
|
"settings.openchamber.visual.field.interfaceFont": "Fuente de la interfaz",
|
||||||
|
"settings.openchamber.visual.field.selectInterfaceFontAria": "Seleccionar fuente de interfaz",
|
||||||
|
"settings.openchamber.visual.actions.resetInterfaceFontAria": "Restablecer fuente de interfaz",
|
||||||
"settings.openchamber.visual.field.fontSizePercentageAria": "Porcentaje de tamaño de fuente",
|
"settings.openchamber.visual.field.fontSizePercentageAria": "Porcentaje de tamaño de fuente",
|
||||||
"settings.openchamber.visual.actions.resetFontSizeAria": "Restablecer tamaño de fuente",
|
"settings.openchamber.visual.actions.resetFontSizeAria": "Restablecer tamaño de fuente",
|
||||||
"settings.openchamber.visual.field.terminalFontSize": "Tamaño de fuente del terminal",
|
"settings.openchamber.visual.field.terminalFontSize": "Tamaño de fuente del terminal",
|
||||||
|
"settings.openchamber.visual.field.codeFont": "Fuente de código",
|
||||||
|
"settings.openchamber.visual.field.selectCodeFontAria": "Seleccionar fuente de código",
|
||||||
|
"settings.openchamber.visual.actions.resetCodeFontAria": "Restablecer fuente de código",
|
||||||
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Restablecer tamaño de fuente del terminal",
|
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Restablecer tamaño de fuente del terminal",
|
||||||
"settings.openchamber.visual.field.spacingDensity": "Densidad de espaciado",
|
"settings.openchamber.visual.field.spacingDensity": "Densidad de espaciado",
|
||||||
"settings.openchamber.visual.actions.resetSpacingAria": "Restablecer espaciado",
|
"settings.openchamber.visual.actions.resetSpacingAria": "Restablecer espaciado",
|
||||||
|
|||||||
@@ -1363,9 +1363,15 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Selecionar orientação",
|
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Selecionar orientação",
|
||||||
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Redefinir orientação de instalação",
|
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Redefinir orientação de instalação",
|
||||||
"settings.openchamber.visual.field.interfaceFontSize": "Tamanho base da interface",
|
"settings.openchamber.visual.field.interfaceFontSize": "Tamanho base da interface",
|
||||||
|
"settings.openchamber.visual.field.interfaceFont": "Fonte da interface",
|
||||||
|
"settings.openchamber.visual.field.selectInterfaceFontAria": "Selecionar fonte da interface",
|
||||||
|
"settings.openchamber.visual.actions.resetInterfaceFontAria": "Redefinir fonte da interface",
|
||||||
"settings.openchamber.visual.field.fontSizePercentageAria": "Porcentagem do tamanho base",
|
"settings.openchamber.visual.field.fontSizePercentageAria": "Porcentagem do tamanho base",
|
||||||
"settings.openchamber.visual.actions.resetFontSizeAria": "Redefinir tamanho base",
|
"settings.openchamber.visual.actions.resetFontSizeAria": "Redefinir tamanho base",
|
||||||
"settings.openchamber.visual.field.terminalFontSize": "Tamanho base do terminal",
|
"settings.openchamber.visual.field.terminalFontSize": "Tamanho base do terminal",
|
||||||
|
"settings.openchamber.visual.field.codeFont": "Fonte do código",
|
||||||
|
"settings.openchamber.visual.field.selectCodeFontAria": "Selecionar fonte do código",
|
||||||
|
"settings.openchamber.visual.actions.resetCodeFontAria": "Redefinir fonte do código",
|
||||||
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Redefinir tamanho base do terminal",
|
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Redefinir tamanho base do terminal",
|
||||||
"settings.openchamber.visual.field.spacingDensity": "Densidade de espaçamento",
|
"settings.openchamber.visual.field.spacingDensity": "Densidade de espaçamento",
|
||||||
"settings.openchamber.visual.actions.resetSpacingAria": "Redefinir espaçamento",
|
"settings.openchamber.visual.actions.resetSpacingAria": "Redefinir espaçamento",
|
||||||
|
|||||||
@@ -1363,9 +1363,15 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Вибрати орієнтацію",
|
"settings.openchamber.visual.field.selectOrientationPlaceholder": "Вибрати орієнтацію",
|
||||||
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Скинути орієнтацію встановлення",
|
"settings.openchamber.visual.actions.resetInstallOrientationAria": "Скинути орієнтацію встановлення",
|
||||||
"settings.openchamber.visual.field.interfaceFontSize": "Розмір шрифту інтерфейсу",
|
"settings.openchamber.visual.field.interfaceFontSize": "Розмір шрифту інтерфейсу",
|
||||||
|
"settings.openchamber.visual.field.interfaceFont": "Шрифт інтерфейсу",
|
||||||
|
"settings.openchamber.visual.field.selectInterfaceFontAria": "Вибрати шрифт інтерфейсу",
|
||||||
|
"settings.openchamber.visual.actions.resetInterfaceFontAria": "Скинути шрифт інтерфейсу",
|
||||||
"settings.openchamber.visual.field.fontSizePercentageAria": "Розмір шрифту у відсотках",
|
"settings.openchamber.visual.field.fontSizePercentageAria": "Розмір шрифту у відсотках",
|
||||||
"settings.openchamber.visual.actions.resetFontSizeAria": "Скинути розмір шрифту",
|
"settings.openchamber.visual.actions.resetFontSizeAria": "Скинути розмір шрифту",
|
||||||
"settings.openchamber.visual.field.terminalFontSize": "Розмір шрифту терміналу",
|
"settings.openchamber.visual.field.terminalFontSize": "Розмір шрифту терміналу",
|
||||||
|
"settings.openchamber.visual.field.codeFont": "Шрифт коду",
|
||||||
|
"settings.openchamber.visual.field.selectCodeFontAria": "Вибрати шрифт коду",
|
||||||
|
"settings.openchamber.visual.actions.resetCodeFontAria": "Скинути шрифт коду",
|
||||||
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Скинути розмір шрифту терміналу",
|
"settings.openchamber.visual.actions.resetTerminalFontSizeAria": "Скинути розмір шрифту терміналу",
|
||||||
"settings.openchamber.visual.field.spacingDensity": "Щільність інтервалу",
|
"settings.openchamber.visual.field.spacingDensity": "Щільність інтервалу",
|
||||||
"settings.openchamber.visual.actions.resetSpacingAria": "Скинути інтервал",
|
"settings.openchamber.visual.actions.resetSpacingAria": "Скинути інтервал",
|
||||||
|
|||||||
@@ -1363,9 +1363,15 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.visual.field.selectOrientationPlaceholder': '选择方向',
|
'settings.openchamber.visual.field.selectOrientationPlaceholder': '选择方向',
|
||||||
'settings.openchamber.visual.actions.resetInstallOrientationAria': '重置安装方向',
|
'settings.openchamber.visual.actions.resetInstallOrientationAria': '重置安装方向',
|
||||||
'settings.openchamber.visual.field.interfaceFontSize': '界面字体大小',
|
'settings.openchamber.visual.field.interfaceFontSize': '界面字体大小',
|
||||||
|
'settings.openchamber.visual.field.interfaceFont': '界面字体',
|
||||||
|
'settings.openchamber.visual.field.selectInterfaceFontAria': '选择界面字体',
|
||||||
|
'settings.openchamber.visual.actions.resetInterfaceFontAria': '重置界面字体',
|
||||||
'settings.openchamber.visual.field.fontSizePercentageAria': '字体大小百分比',
|
'settings.openchamber.visual.field.fontSizePercentageAria': '字体大小百分比',
|
||||||
'settings.openchamber.visual.actions.resetFontSizeAria': '重置字体大小',
|
'settings.openchamber.visual.actions.resetFontSizeAria': '重置字体大小',
|
||||||
'settings.openchamber.visual.field.terminalFontSize': '终端字体大小',
|
'settings.openchamber.visual.field.terminalFontSize': '终端字体大小',
|
||||||
|
'settings.openchamber.visual.field.codeFont': '代码字体',
|
||||||
|
'settings.openchamber.visual.field.selectCodeFontAria': '选择代码字体',
|
||||||
|
'settings.openchamber.visual.actions.resetCodeFontAria': '重置代码字体',
|
||||||
'settings.openchamber.visual.actions.resetTerminalFontSizeAria': '重置终端字体大小',
|
'settings.openchamber.visual.actions.resetTerminalFontSizeAria': '重置终端字体大小',
|
||||||
'settings.openchamber.visual.field.spacingDensity': '间距密度',
|
'settings.openchamber.visual.field.spacingDensity': '间距密度',
|
||||||
'settings.openchamber.visual.actions.resetSpacingAria': '重置间距',
|
'settings.openchamber.visual.actions.resetSpacingAria': '重置间距',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { DesktopSettings } from '@/lib/desktop';
|
import type { DesktopSettings } from '@/lib/desktop';
|
||||||
import { createProjectIdFromPath } from '@/lib/projectId';
|
import { createProjectIdFromPath } from '@/lib/projectId';
|
||||||
import { useUIStore } from '@/stores/useUIStore';
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { isMonoFontOption, isUiFontOption } from '@/lib/fontOptions';
|
||||||
import { useMessageQueueStore } from '@/stores/messageQueueStore';
|
import { useMessageQueueStore } from '@/stores/messageQueueStore';
|
||||||
import { setDirectoryShowHidden } from '@/lib/directoryShowHidden';
|
import { setDirectoryShowHidden } from '@/lib/directoryShowHidden';
|
||||||
import { setFilesViewShowGitignored } from '@/lib/filesViewShowGitignored';
|
import { setFilesViewShowGitignored } from '@/lib/filesViewShowGitignored';
|
||||||
@@ -439,6 +440,12 @@ const applyDesktopUiPreferences = (settings: DesktopSettings) => {
|
|||||||
if (typeof settings.terminalFontSize === 'number' && Number.isFinite(settings.terminalFontSize) && settings.terminalFontSize !== store.terminalFontSize) {
|
if (typeof settings.terminalFontSize === 'number' && Number.isFinite(settings.terminalFontSize) && settings.terminalFontSize !== store.terminalFontSize) {
|
||||||
store.setTerminalFontSize(settings.terminalFontSize);
|
store.setTerminalFontSize(settings.terminalFontSize);
|
||||||
}
|
}
|
||||||
|
if (isUiFontOption(settings.uiFont) && settings.uiFont !== store.uiFont) {
|
||||||
|
store.setUiFont(settings.uiFont);
|
||||||
|
}
|
||||||
|
if (isMonoFontOption(settings.monoFont) && settings.monoFont !== store.monoFont) {
|
||||||
|
store.setMonoFont(settings.monoFont);
|
||||||
|
}
|
||||||
if (typeof settings.padding === 'number' && Number.isFinite(settings.padding) && settings.padding !== store.padding) {
|
if (typeof settings.padding === 'number' && Number.isFinite(settings.padding) && settings.padding !== store.padding) {
|
||||||
store.setPadding(settings.padding);
|
store.setPadding(settings.padding);
|
||||||
}
|
}
|
||||||
@@ -852,6 +859,12 @@ const sanitizeWebSettings = (payload: unknown): DesktopSettings | null => {
|
|||||||
if (typeof candidate.terminalFontSize === 'number' && Number.isFinite(candidate.terminalFontSize)) {
|
if (typeof candidate.terminalFontSize === 'number' && Number.isFinite(candidate.terminalFontSize)) {
|
||||||
result.terminalFontSize = candidate.terminalFontSize;
|
result.terminalFontSize = candidate.terminalFontSize;
|
||||||
}
|
}
|
||||||
|
if (isUiFontOption(candidate.uiFont)) {
|
||||||
|
result.uiFont = candidate.uiFont;
|
||||||
|
}
|
||||||
|
if (isMonoFontOption(candidate.monoFont)) {
|
||||||
|
result.monoFont = candidate.monoFont;
|
||||||
|
}
|
||||||
if (typeof candidate.padding === 'number' && Number.isFinite(candidate.padding)) {
|
if (typeof candidate.padding === 'number' && Number.isFinite(candidate.padding)) {
|
||||||
result.padding = candidate.padding;
|
result.padding = candidate.padding;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { SidebarSection } from '@/constants/sidebar';
|
|||||||
import { getSafeStorage } from './utils/safeStorage';
|
import { getSafeStorage } from './utils/safeStorage';
|
||||||
import { SEMANTIC_TYPOGRAPHY, getTypographyVariable, type SemanticTypographyKey } from '@/lib/typography';
|
import { SEMANTIC_TYPOGRAPHY, getTypographyVariable, type SemanticTypographyKey } from '@/lib/typography';
|
||||||
import type { ShortcutCombo } from '@/lib/shortcuts';
|
import type { ShortcutCombo } from '@/lib/shortcuts';
|
||||||
|
import { DEFAULT_MONO_FONT, DEFAULT_UI_FONT, type MonoFontOption, type UiFontOption } from '@/lib/fontOptions';
|
||||||
|
|
||||||
export type MainTab = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files';
|
export type MainTab = 'chat' | 'plan' | 'git' | 'diff' | 'terminal' | 'files';
|
||||||
export type RightSidebarTab = 'git' | 'files' | 'context';
|
export type RightSidebarTab = 'git' | 'files' | 'context';
|
||||||
@@ -512,6 +513,8 @@ interface UIStore {
|
|||||||
messageLimit: number;
|
messageLimit: number;
|
||||||
fontSize: number;
|
fontSize: number;
|
||||||
terminalFontSize: number;
|
terminalFontSize: number;
|
||||||
|
uiFont: UiFontOption;
|
||||||
|
monoFont: MonoFontOption;
|
||||||
padding: number;
|
padding: number;
|
||||||
cornerRadius: number;
|
cornerRadius: number;
|
||||||
inputBarOffset: number;
|
inputBarOffset: number;
|
||||||
@@ -633,6 +636,8 @@ interface UIStore {
|
|||||||
setMessageLimit: (value: number) => void;
|
setMessageLimit: (value: number) => void;
|
||||||
setFontSize: (size: number) => void;
|
setFontSize: (size: number) => void;
|
||||||
setTerminalFontSize: (size: number) => void;
|
setTerminalFontSize: (size: number) => void;
|
||||||
|
setUiFont: (font: UiFontOption) => void;
|
||||||
|
setMonoFont: (font: MonoFontOption) => void;
|
||||||
setPadding: (size: number) => void;
|
setPadding: (size: number) => void;
|
||||||
setCornerRadius: (radius: number) => void;
|
setCornerRadius: (radius: number) => void;
|
||||||
setInputBarOffset: (offset: number) => void;
|
setInputBarOffset: (offset: number) => void;
|
||||||
@@ -750,6 +755,8 @@ export const useUIStore = create<UIStore>()(
|
|||||||
messageLimit: 200,
|
messageLimit: 200,
|
||||||
fontSize: 100,
|
fontSize: 100,
|
||||||
terminalFontSize: 13,
|
terminalFontSize: 13,
|
||||||
|
uiFont: DEFAULT_UI_FONT,
|
||||||
|
monoFont: DEFAULT_MONO_FONT,
|
||||||
padding: 100,
|
padding: 100,
|
||||||
cornerRadius: 18,
|
cornerRadius: 18,
|
||||||
inputBarOffset: 0,
|
inputBarOffset: 0,
|
||||||
@@ -1370,6 +1377,14 @@ export const useUIStore = create<UIStore>()(
|
|||||||
set({ terminalFontSize: clamped });
|
set({ terminalFontSize: clamped });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setUiFont: (font) => {
|
||||||
|
set({ uiFont: font });
|
||||||
|
},
|
||||||
|
|
||||||
|
setMonoFont: (font) => {
|
||||||
|
set({ monoFont: font });
|
||||||
|
},
|
||||||
|
|
||||||
setPadding: (size) => {
|
setPadding: (size) => {
|
||||||
// Clamp between 50% and 200%
|
// Clamp between 50% and 200%
|
||||||
const clampedSize = Math.max(50, Math.min(200, size));
|
const clampedSize = Math.max(50, Math.min(200, size));
|
||||||
@@ -1892,6 +1907,8 @@ export const useUIStore = create<UIStore>()(
|
|||||||
messageLimit: state.messageLimit,
|
messageLimit: state.messageLimit,
|
||||||
fontSize: state.fontSize,
|
fontSize: state.fontSize,
|
||||||
terminalFontSize: state.terminalFontSize,
|
terminalFontSize: state.terminalFontSize,
|
||||||
|
uiFont: state.uiFont,
|
||||||
|
monoFont: state.monoFont,
|
||||||
padding: state.padding,
|
padding: state.padding,
|
||||||
cornerRadius: state.cornerRadius,
|
cornerRadius: state.cornerRadius,
|
||||||
favoriteModels: state.favoriteModels,
|
favoriteModels: state.favoriteModels,
|
||||||
|
|||||||
Reference in New Issue
Block a user