Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { isElectron } from "@/lib/electron";
import { Toaster } from "@/components/sonner";
import { Auth, getStoredAuth, clearStoredAuth } from "@/auth/Auth";
import { getUserInfo, getCurrentToken, appReadyPromise } from "@/main-axios";
import { applyAccentColor, applyFontSize } from "@/lib/theme";
import { applyAccentColor, applyFontSize, applyUiFont } from "@/lib/theme";
import { installElectronWheelZoomGuard } from "@/lib/electron-wheel-zoom";
import type { FontSizeId } from "@/types/ui-types";
import type { FontSizeId, UiFontId } from "@/types/ui-types";
import { useServiceWorker } from "@/hooks/use-service-worker";
import { useTranslation } from "react-i18next";
import { UiPreferencesProvider } from "@/contexts/UiPreferencesContext";
Expand Down Expand Up @@ -202,6 +202,10 @@ function App() {
"termix-font-size",
) as FontSizeId | null;
applyFontSize(savedSize ?? "md");
applyUiFont(
(localStorage.getItem("termix-ui-font") as UiFontId | null) ??
"jetbrains-mono",
);
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
Expand Down
6 changes: 6 additions & 0 deletions src/types/ui-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ export type ThemeId =
| "one-dark"
| "gruvbox";
export type FontSizeId = "xs" | "sm" | "md" | "lg" | "xl";
export type UiFontId =
| "jetbrains-mono"
| "system-sans"
| "fira-code"
| "source-code-pro"
| "caskaydia-cove";

export type ToolsTab =
"ssh-tools" | "snippets" | "macros" | "history" | "split-screen";
Expand Down
1 change: 1 addition & 0 deletions src/ui/AppShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,7 @@ export function AppShell({
const SNAPSHOT_KEYS = [
"termix-accent",
"termix-font-size",
"termix-ui-font",
"i18nextLng",
"commandAutocomplete",
"commandPaletteShortcutEnabled",
Expand Down
2 changes: 1 addition & 1 deletion src/ui/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

@theme inline {
--font-heading: var(--font-mono);
--font-mono: "JetBrains Mono Variable", monospace;
--font-mono: var(--font-ui, "JetBrains Mono Variable", monospace);
--color-accent-brand: var(--accent-brand);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
Expand Down
35 changes: 35 additions & 0 deletions src/ui/lib/theme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
DashboardCardConfig,
FontSizeId,
UiFontId,
SplitMode,
} from "@/types/ui-types";

Expand Down Expand Up @@ -89,6 +90,40 @@ export function applyFontSize(id: FontSizeId) {
localStorage.setItem("termix-font-size", id);
}

export const UI_FONTS: { id: UiFontId; label: string; family: string }[] = [
{
id: "jetbrains-mono",
label: "JetBrains Mono",
family: '"JetBrains Mono Variable", monospace',
},
{
id: "system-sans",
label: "System Sans",
family: "ui-sans-serif, system-ui, sans-serif",
},
{
id: "fira-code",
label: "Fira Code",
family: '"Fira Code", monospace',
},
{
id: "source-code-pro",
label: "Source Code Pro",
family: '"Source Code Pro", monospace',
},
{
id: "caskaydia-cove",
label: "Caskaydia Cove",
family: '"Caskaydia Cove Nerd Font Mono", monospace',
},
];

export function applyUiFont(id: UiFontId) {
const font = UI_FONTS.find((candidate) => candidate.id === id) ?? UI_FONTS[0];
document.documentElement.style.setProperty("--font-ui", font.family);
localStorage.setItem("termix-ui-font", font.id);
}

export const FOLDER_COLORS = [
"#ef4444",
"#f97316",
Expand Down
2 changes: 2 additions & 0 deletions src/ui/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4150,6 +4150,8 @@
"languageLabel": "Language",
"themeLabel": "Theme",
"fontSizeLabel": "Font Size",
"interfaceFontLabel": "Interface Font",
"interfaceFontDescription": "Changes the application interface font. Terminal profiles keep their own font settings.",
"accentColorLabel": "Accent Color",
"settingsTerminal": "Terminal",
"commandAutocomplete": "Command Autocomplete",
Expand Down
45 changes: 44 additions & 1 deletion src/ui/sidebar/UserProfilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ import {
ACCENT_PRESET_COLORS,
applyAccentColor,
applyFontSize,
applyUiFont,
FONT_SIZES,
UI_FONTS,
} from "@/lib/theme";
import type { ApiKey } from "@/main-axios";
import { useTheme } from "@/components/theme-provider";
import type { FontSizeId, ThemeId } from "@/types/ui-types";
import type { FontSizeId, ThemeId, UiFontId } from "@/types/ui-types";
import { toast } from "sonner";
import { changeAppLanguage, normalizeLanguageCode } from "@/i18n/i18n";
import { clearLocalAdaptivePreferences } from "@/lib/local-adaptive-preferences";
Expand Down Expand Up @@ -597,6 +599,10 @@ export function UserProfilePanel({
const [fontSize, setFontSize] = useState<FontSizeId>(
() => (localStorage.getItem("termix-font-size") as FontSizeId) ?? "md",
);
const [uiFont, setUiFont] = useState<UiFontId>(
() =>
(localStorage.getItem("termix-ui-font") as UiFontId) ?? "jetbrains-mono",
);
const [language, setLanguage] = useState(() =>
normalizeLanguageCode(localStorage.getItem("i18nextLng")),
);
Expand Down Expand Up @@ -831,6 +837,7 @@ export function UserProfilePanel({
const SNAPSHOT_KEYS = [
"termix-accent",
"termix-font-size",
"termix-ui-font",
"i18nextLng",
"commandAutocomplete",
"commandPaletteShortcutEnabled",
Expand Down Expand Up @@ -961,6 +968,8 @@ export function UserProfilePanel({
setTheme("system");
setFontSize("md");
applyFontSize("md");
setUiFont("jetbrains-mono");
applyUiFont("jetbrains-mono");
setAccentColor(DEFAULT_ACCENT);
setCustomColorInput(DEFAULT_ACCENT);
localStorage.setItem("termix-accent", DEFAULT_ACCENT);
Expand Down Expand Up @@ -1047,6 +1056,12 @@ export function UserProfilePanel({
setFontSize(restoredFontSize);
applyFontSize(restoredFontSize);

const restoredUiFont =
(restore("termix-ui-font", "jetbrains-mono") as UiFontId) ??
"jetbrains-mono";
setUiFont(restoredUiFont);
applyUiFont(restoredUiFont);

const restoredAccent = restore("termix-accent", "#f59145") ?? "#f59145";
setAccentColor(restoredAccent);
setCustomColorInput(restoredAccent);
Expand Down Expand Up @@ -1156,6 +1171,11 @@ export function UserProfilePanel({
if (storageMode === "cloud") saveToCloud({ fontSize: id });
}

function handleUiFontChange(id: UiFontId) {
setUiFont(id);
applyUiFont(id);
}

function handleLanguageChange(code: string) {
void changeAppLanguage(code)
.then((language) => {
Expand Down Expand Up @@ -1723,6 +1743,29 @@ export function UserProfilePanel({
</div>
</div>

<div className="flex flex-col gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Type className="size-3" />
{t("newUi.sidebar.userProfile.interfaceFontLabel")}
</span>
<select
value={uiFont}
onChange={(event) =>
handleUiFontChange(event.target.value as UiFontId)
}
className="px-2.5 py-1.5 text-xs bg-background border border-border text-foreground outline-none focus:ring-1 focus:ring-ring w-full"
>
{UI_FONTS.map((font) => (
<option key={font.id} value={font.id}>
{font.label}
</option>
))}
</select>
<span className="text-[10px] text-muted-foreground">
{t("newUi.sidebar.userProfile.interfaceFontDescription")}
</span>
</div>

<div className="flex flex-col gap-1.5">
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5">
<Type className="size-3" />
Expand Down
24 changes: 24 additions & 0 deletions src/ui/tests/lib/ui-font.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { beforeEach, describe, expect, it } from "vitest";
import { applyUiFont } from "../../lib/theme";

describe("applyUiFont", () => {
beforeEach(() => {
localStorage.clear();
document.documentElement.style.removeProperty("--font-ui");
});

it("applies and persists a readable system font", () => {
applyUiFont("system-sans");

expect(document.documentElement.style.getPropertyValue("--font-ui")).toBe(
"ui-sans-serif, system-ui, sans-serif",
);
expect(localStorage.getItem("termix-ui-font")).toBe("system-sans");
});

it("falls back to JetBrains Mono for an unknown stored value", () => {
applyUiFont("unknown" as never);

expect(localStorage.getItem("termix-ui-font")).toBe("jetbrains-mono");
});
});
Loading