diff --git a/apps/desktop/src/lib/trpc/routers/settings/font-settings.test.ts b/apps/desktop/src/lib/trpc/routers/settings/font-settings.test.ts new file mode 100644 index 00000000000..2e843889141 --- /dev/null +++ b/apps/desktop/src/lib/trpc/routers/settings/font-settings.test.ts @@ -0,0 +1,167 @@ +import { describe, expect, it } from "bun:test"; +import { + setFontSettingsSchema, + transformFontSettings, +} from "./font-settings.utils"; + +describe("font settings validation", () => { + describe("font size validation (range 10-24)", () => { + it("accepts font size at minimum (10)", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: 10, + }); + expect(result.success).toBe(true); + }); + + it("accepts font size at maximum (24)", () => { + const result = setFontSettingsSchema.safeParse({ + editorFontSize: 24, + }); + expect(result.success).toBe(true); + }); + + it("accepts font size in the middle of range", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: 14, + editorFontSize: 16, + }); + expect(result.success).toBe(true); + }); + + it("rejects font size below minimum (< 10)", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: 9, + }); + expect(result.success).toBe(false); + }); + + it("rejects font size of 0", () => { + const result = setFontSettingsSchema.safeParse({ + editorFontSize: 0, + }); + expect(result.success).toBe(false); + }); + + it("rejects font size above maximum (> 24)", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: 25, + }); + expect(result.success).toBe(false); + }); + + it("rejects very large font size", () => { + const result = setFontSettingsSchema.safeParse({ + editorFontSize: 100, + }); + expect(result.success).toBe(false); + }); + + it("rejects non-integer font sizes", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: 14.5, + }); + expect(result.success).toBe(false); + }); + + it("accepts null font size (reset)", () => { + const result = setFontSettingsSchema.safeParse({ + terminalFontSize: null, + editorFontSize: null, + }); + expect(result.success).toBe(true); + }); + }); + + describe("font family trimming", () => { + it("trims whitespace from font family", () => { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: " JetBrains Mono ", + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBe("JetBrains Mono"); + }); + + it("trims whitespace from editor font family", () => { + const input = setFontSettingsSchema.parse({ + editorFontFamily: " Fira Code ", + }); + const result = transformFontSettings(input); + expect(result.editorFontFamily).toBe("Fira Code"); + }); + + it("accepts valid font families without modification", () => { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: "JetBrains Mono", + editorFontFamily: "Fira Code", + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBe("JetBrains Mono"); + expect(result.editorFontFamily).toBe("Fira Code"); + }); + + it("accepts common monospace fonts", () => { + const fonts = [ + "JetBrains Mono", + "Fira Code", + "Source Code Pro", + "Cascadia Code", + "IBM Plex Mono", + "Hack", + "Inconsolata", + ]; + + for (const font of fonts) { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: font, + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBe(font); + } + }); + }); + + describe("empty string as null (reset)", () => { + it("treats empty string font family as null", () => { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: "", + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBeNull(); + }); + + it("treats whitespace-only font family as null", () => { + const input = setFontSettingsSchema.parse({ + editorFontFamily: " ", + }); + const result = transformFontSettings(input); + expect(result.editorFontFamily).toBeNull(); + }); + + it("treats null font family as null", () => { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: null, + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBeNull(); + }); + }); + + describe("partial updates", () => { + it("only includes provided fields in the result", () => { + const input = setFontSettingsSchema.parse({ + terminalFontFamily: "Fira Code", + }); + const result = transformFontSettings(input); + expect(result.terminalFontFamily).toBe("Fira Code"); + expect(result).not.toHaveProperty("editorFontFamily"); + expect(result).not.toHaveProperty("terminalFontSize"); + expect(result).not.toHaveProperty("editorFontSize"); + }); + + it("accepts empty input (no changes)", () => { + const input = setFontSettingsSchema.parse({}); + const result = transformFontSettings(input); + expect(Object.keys(result)).toHaveLength(0); + }); + }); +}); diff --git a/apps/desktop/src/lib/trpc/routers/settings/font-settings.utils.ts b/apps/desktop/src/lib/trpc/routers/settings/font-settings.utils.ts new file mode 100644 index 00000000000..0b471592434 --- /dev/null +++ b/apps/desktop/src/lib/trpc/routers/settings/font-settings.utils.ts @@ -0,0 +1,31 @@ +import { z } from "zod"; + +export const setFontSettingsSchema = z.object({ + terminalFontFamily: z.string().max(500).nullable().optional(), + terminalFontSize: z.number().int().min(10).max(24).nullable().optional(), + editorFontFamily: z.string().max(500).nullable().optional(), + editorFontSize: z.number().int().min(10).max(24).nullable().optional(), +}); + +export type SetFontSettingsInput = z.infer; + +export function transformFontSettings( + input: SetFontSettingsInput, +): Record { + const set: Record = {}; + + if (input.terminalFontFamily !== undefined) { + set.terminalFontFamily = input.terminalFontFamily?.trim() || null; + } + if (input.terminalFontSize !== undefined) { + set.terminalFontSize = input.terminalFontSize; + } + if (input.editorFontFamily !== undefined) { + set.editorFontFamily = input.editorFontFamily?.trim() || null; + } + if (input.editorFontSize !== undefined) { + set.editorFontSize = input.editorFontSize; + } + + return set; +} diff --git a/apps/desktop/src/lib/trpc/routers/settings/index.ts b/apps/desktop/src/lib/trpc/routers/settings/index.ts index d9ff55e10b7..a4562856f33 100644 --- a/apps/desktop/src/lib/trpc/routers/settings/index.ts +++ b/apps/desktop/src/lib/trpc/routers/settings/index.ts @@ -20,6 +20,10 @@ import { DEFAULT_RINGTONE_ID, RINGTONES } from "shared/ringtones"; import { z } from "zod"; import { publicProcedure, router } from "../.."; import { getGitAuthorName, getGitHubUsername } from "../workspaces/utils/git"; +import { + setFontSettingsSchema, + transformFontSettings, +} from "./font-settings.utils"; const VALID_RINGTONE_IDS = RINGTONES.map((r) => r.id); @@ -495,6 +499,37 @@ export const createSettingsRouter = () => { return { success: true }; }), + getFontSettings: publicProcedure.query(() => { + const row = getSettings(); + return { + terminalFontFamily: row.terminalFontFamily ?? null, + terminalFontSize: row.terminalFontSize ?? null, + editorFontFamily: row.editorFontFamily ?? null, + editorFontSize: row.editorFontSize ?? null, + }; + }), + + setFontSettings: publicProcedure + .input(setFontSettingsSchema) + .mutation(({ input }) => { + const set = transformFontSettings(input); + + if (Object.keys(set).length === 0) { + return { success: true }; + } + + localDb + .insert(settings) + .values({ id: 1, ...set }) + .onConflictDoUpdate({ + target: settings.id, + set, + }) + .run(); + + return { success: true }; + }), + // TODO: remove telemetry procedures once telemetry_enabled column is dropped getTelemetryEnabled: publicProcedure.query(() => { return true; diff --git a/apps/desktop/src/renderer/providers/MonacoProvider/MonacoProvider.tsx b/apps/desktop/src/renderer/providers/MonacoProvider/MonacoProvider.tsx index 3264e271422..0876d05f6db 100644 --- a/apps/desktop/src/renderer/providers/MonacoProvider/MonacoProvider.tsx +++ b/apps/desktop/src/renderer/providers/MonacoProvider/MonacoProvider.tsx @@ -6,7 +6,8 @@ import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker"; import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker"; import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker"; import type React from "react"; -import { createContext, useContext, useEffect, useState } from "react"; +import { createContext, useContext, useEffect, useMemo, useState } from "react"; +import { electronTrpc } from "renderer/lib/electron-trpc"; import { useMonacoTheme } from "renderer/stores/theme"; self.MonacoEnvironment = { @@ -127,6 +128,31 @@ export const MONACO_EDITOR_OPTIONS = { }, }; +export function useMonacoEditorOptions() { + const { data: fontSettings } = electronTrpc.settings.getFontSettings.useQuery( + undefined, + { + staleTime: 30_000, + }, + ); + + return useMemo(() => { + if (!fontSettings) return MONACO_EDITOR_OPTIONS; + const fontSize = + fontSettings.editorFontSize ?? MONACO_EDITOR_OPTIONS.fontSize; + return { + ...MONACO_EDITOR_OPTIONS, + ...(fontSettings.editorFontFamily && { + fontFamily: fontSettings.editorFontFamily, + }), + ...(fontSettings.editorFontSize != null && { + fontSize, + lineHeight: Math.round(fontSize * 1.5), + }), + }; + }, [fontSettings]); +} + export function registerSaveAction( editor: monaco.editor.IStandaloneCodeEditor, onSave: () => void, diff --git a/apps/desktop/src/renderer/providers/MonacoProvider/index.ts b/apps/desktop/src/renderer/providers/MonacoProvider/index.ts index 9c0a21f3940..65656ed95f7 100644 --- a/apps/desktop/src/renderer/providers/MonacoProvider/index.ts +++ b/apps/desktop/src/renderer/providers/MonacoProvider/index.ts @@ -4,5 +4,6 @@ export { monaco, registerSaveAction, SUPERSET_THEME, + useMonacoEditorOptions, useMonacoReady, } from "./MonacoProvider"; diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx b/apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx index 7863aa7495c..f566bbc2826 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx @@ -1,3 +1,5 @@ +import { Button } from "@superset/ui/button"; +import { Input } from "@superset/ui/input"; import { Select, SelectContent, @@ -5,6 +7,13 @@ import { SelectTrigger, SelectValue, } from "@superset/ui/select"; +import { useCallback, useEffect, useState } from "react"; +import { electronTrpc } from "renderer/lib/electron-trpc"; +import { MONACO_EDITOR_OPTIONS } from "renderer/providers/MonacoProvider"; +import { + DEFAULT_TERMINAL_FONT_FAMILY, + DEFAULT_TERMINAL_FONT_SIZE, +} from "renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/config"; import { type MarkdownStyle, SYSTEM_THEME_ID, @@ -23,6 +32,39 @@ import { import { SystemThemeCard } from "./components/SystemThemeCard"; import { ThemeCard } from "./components/ThemeCard"; +const DEFAULT_EDITOR_FONT_FAMILY = MONACO_EDITOR_OPTIONS.fontFamily; +const DEFAULT_EDITOR_FONT_SIZE = MONACO_EDITOR_OPTIONS.fontSize; + +const FONT_PREVIEW_TEXT = + "The quick brown fox jumps over the lazy dog.\n0O1lI {}[]() => !== +- @#$%"; + +function FontPreview({ + fontFamily, + fontSize, + variant, +}: { + fontFamily: string; + fontSize: number; + variant: "editor" | "terminal"; +}) { + const isTerminal = variant === "terminal"; + return ( +
+ {FONT_PREVIEW_TEXT} +
+ ); +} + interface AppearanceSettingsProps { visibleItems?: SettingItemId[] | null; } @@ -40,6 +82,14 @@ export function AppearanceSettings({ visibleItems }: AppearanceSettingsProps) { SETTING_ITEM_ID.APPEARANCE_CUSTOM_THEMES, visibleItems, ); + const showEditorFont = isItemVisible( + SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT, + visibleItems, + ); + const showTerminalFont = isItemVisible( + SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT, + visibleItems, + ); const activeThemeId = useThemeId(); const setTheme = useSetTheme(); @@ -49,6 +99,114 @@ export function AppearanceSettings({ visibleItems }: AppearanceSettingsProps) { const allThemes = [...builtInThemes, ...customThemes]; + const utils = electronTrpc.useUtils(); + + const { data: fontSettings, isLoading: isFontLoading } = + electronTrpc.settings.getFontSettings.useQuery(); + + const setFontSettings = electronTrpc.settings.setFontSettings.useMutation({ + onMutate: async (input) => { + await utils.settings.getFontSettings.cancel(); + const previous = utils.settings.getFontSettings.getData(); + utils.settings.getFontSettings.setData(undefined, (old) => ({ + terminalFontFamily: old?.terminalFontFamily ?? null, + terminalFontSize: old?.terminalFontSize ?? null, + editorFontFamily: old?.editorFontFamily ?? null, + editorFontSize: old?.editorFontSize ?? null, + ...input, + })); + return { previous }; + }, + onError: (_err, _vars, context) => { + if (context?.previous !== undefined) { + utils.settings.getFontSettings.setData(undefined, context.previous); + } + }, + onSettled: () => { + utils.settings.getFontSettings.invalidate(); + }, + }); + + const handleEditorFontFamilyBlur = useCallback( + (e: React.FocusEvent) => { + const value = e.target.value.trim(); + setFontSettings.mutate({ + editorFontFamily: value || null, + }); + }, + [setFontSettings], + ); + + const handleEditorFontSizeBlur = useCallback( + (e: React.FocusEvent) => { + const value = Number.parseInt(e.target.value, 10); + if (!Number.isNaN(value) && value >= 10 && value <= 24) { + setFontSettings.mutate({ editorFontSize: value }); + } + }, + [setFontSettings], + ); + + const handleTerminalFontFamilyBlur = useCallback( + (e: React.FocusEvent) => { + const value = e.target.value.trim(); + setFontSettings.mutate({ + terminalFontFamily: value || null, + }); + }, + [setFontSettings], + ); + + const handleTerminalFontSizeBlur = useCallback( + (e: React.FocusEvent) => { + const value = Number.parseInt(e.target.value, 10); + if (!Number.isNaN(value) && value >= 10 && value <= 24) { + setFontSettings.mutate({ terminalFontSize: value }); + } + }, + [setFontSettings], + ); + + const [editorFontDraft, setEditorFontDraft] = useState(null); + const [terminalFontDraft, setTerminalFontDraft] = useState( + null, + ); + const [editorFontSizeDraft, setEditorFontSizeDraft] = useState( + null, + ); + const [terminalFontSizeDraft, setTerminalFontSizeDraft] = useState< + string | null + >(null); + + // biome-ignore lint/correctness/useExhaustiveDependencies: sync draft state when fontSettings changes + useEffect(() => { + setEditorFontSizeDraft(null); + setTerminalFontSizeDraft(null); + }, [fontSettings]); + + const editorPreviewFamily = + editorFontDraft ?? + fontSettings?.editorFontFamily ?? + DEFAULT_EDITOR_FONT_FAMILY; + const editorPreviewSize = + (editorFontSizeDraft != null + ? Number.parseInt(editorFontSizeDraft, 10) + : undefined) || + fontSettings?.editorFontSize || + DEFAULT_EDITOR_FONT_SIZE; + const terminalPreviewFamily = + terminalFontDraft ?? + fontSettings?.terminalFontFamily ?? + DEFAULT_TERMINAL_FONT_FAMILY; + const terminalPreviewSize = + (terminalFontSizeDraft != null + ? Number.parseInt(terminalFontSizeDraft, 10) + : undefined) || + fontSettings?.terminalFontSize || + DEFAULT_TERMINAL_FONT_SIZE; + + const hasPrecedingSection = showTheme || showMarkdown; + return (
@@ -107,8 +265,160 @@ export function AppearanceSettings({ visibleItems }: AppearanceSettingsProps) {
)} + {showEditorFont && ( +
+

Editor Font

+

+ Font used in diff views and file editors +

+
+ setEditorFontDraft(e.target.value)} + onBlur={(e) => { + handleEditorFontFamilyBlur(e); + setEditorFontDraft(null); + }} + disabled={isFontLoading} + className="flex-1" + /> + setEditorFontSizeDraft(e.target.value)} + onBlur={(e) => { + handleEditorFontSizeBlur(e); + setEditorFontSizeDraft(null); + }} + disabled={isFontLoading} + className="w-20" + /> + {(fontSettings?.editorFontFamily || + fontSettings?.editorFontSize) && ( + + )} +
+
+ +
+
+ )} + + {showTerminalFont && ( +
+

Terminal Font

+

+ Font used in terminal panels.{" "} + + Nerd Fonts + {" "} + recommended for shell theme icons. +

+
+ setTerminalFontDraft(e.target.value)} + onBlur={(e) => { + handleTerminalFontFamilyBlur(e); + setTerminalFontDraft(null); + }} + disabled={isFontLoading} + className="flex-1" + /> + setTerminalFontSizeDraft(e.target.value)} + onBlur={(e) => { + handleTerminalFontSizeBlur(e); + setTerminalFontSizeDraft(null); + }} + disabled={isFontLoading} + className="w-20" + /> + {(fontSettings?.terminalFontFamily || + fontSettings?.terminalFontSize) && ( + + )} +
+
+ +
+
+ )} + {showCustomThemes && ( -
+

Custom Themes

Custom theme import coming soon. You'll be able to import JSON diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts new file mode 100644 index 00000000000..5b860fb8946 --- /dev/null +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts @@ -0,0 +1,65 @@ +import { describe, expect, it } from "bun:test"; +import { + SETTING_ITEM_ID, + type SettingsItem, + searchSettings, +} from "./settings-search"; + +function getIds(items: SettingsItem[]): string[] { + return items.map((item) => item.id); +} + +describe("settings search - font settings", () => { + it('searching "font" returns both APPEARANCE_EDITOR_FONT and APPEARANCE_TERMINAL_FONT', () => { + const results = searchSettings("font"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT); + }); + + it('searching "terminal font" returns APPEARANCE_TERMINAL_FONT', () => { + const results = searchSettings("terminal font"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT); + }); + + it('searching "editor" returns APPEARANCE_EDITOR_FONT', () => { + const results = searchSettings("editor"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT); + }); + + it('searching "monospace" returns both font items', () => { + const results = searchSettings("monospace"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT); + }); + + it('searching "Editor Font" is case-insensitive', () => { + const results = searchSettings("Editor Font"); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT); + }); + + it("empty search returns all settings items", () => { + const results = searchSettings(""); + expect(results.length).toBeGreaterThan(0); + const ids = getIds(results); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT); + expect(ids).toContain(SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT); + }); + + it("font items have correct section", () => { + const results = searchSettings("font"); + const editorFont = results.find( + (r) => r.id === SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT, + ); + const terminalFont = results.find( + (r) => r.id === SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT, + ); + + expect(editorFont?.section).toBe("appearance"); + expect(terminalFont?.section).toBe("appearance"); + }); +}); diff --git a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts index b69f8c9093e..89ef2fbbb29 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts @@ -15,6 +15,8 @@ export const SETTING_ITEM_ID = { APPEARANCE_THEME: "appearance-theme", APPEARANCE_MARKDOWN: "appearance-markdown", APPEARANCE_CUSTOM_THEMES: "appearance-custom-themes", + APPEARANCE_EDITOR_FONT: "appearance-editor-font", + APPEARANCE_TERMINAL_FONT: "appearance-terminal-font", RINGTONES_NOTIFICATION: "ringtones-notification", @@ -251,6 +253,42 @@ export const SETTINGS_ITEMS: SettingsItem[] = [ "customize", ], }, + { + id: SETTING_ITEM_ID.APPEARANCE_EDITOR_FONT, + section: "appearance", + title: "Editor Font", + description: "Font used in diff views and file editors", + keywords: [ + "appearance", + "font", + "family", + "size", + "editor", + "diff", + "mono", + "monospace", + "typography", + "custom", + ], + }, + { + id: SETTING_ITEM_ID.APPEARANCE_TERMINAL_FONT, + section: "appearance", + title: "Terminal Font", + description: "Font used in terminal panels", + keywords: [ + "appearance", + "font", + "family", + "size", + "terminal", + "mono", + "monospace", + "typography", + "custom", + "nerd", + ], + }, { id: SETTING_ITEM_ID.RINGTONES_NOTIFICATION, section: "ringtones", diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/DiffViewer/DiffViewer.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/DiffViewer/DiffViewer.tsx index 483d9489caa..09342e7bc36 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/DiffViewer/DiffViewer.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ChangesContent/components/DiffViewer/DiffViewer.tsx @@ -3,9 +3,9 @@ import type * as Monaco from "monaco-editor"; import { useCallback, useEffect, useRef, useState } from "react"; import { LuLoader } from "react-icons/lu"; import { - MONACO_EDITOR_OPTIONS, registerSaveAction, SUPERSET_THEME, + useMonacoEditorOptions, useMonacoReady, } from "renderer/providers/MonacoProvider"; import type { Tab } from "renderer/stores/tabs/types"; @@ -72,6 +72,7 @@ export function DiffViewer({ fitContent = false, }: DiffViewerProps) { const isMonacoReady = useMonacoReady(); + const monacoEditorOptions = useMonacoEditorOptions(); const diffEditorRef = useRef( null, ); @@ -272,7 +273,7 @@ export function DiffViewer({

} options={{ - ...MONACO_EDITOR_OPTIONS, + ...monacoEditorOptions, lineNumbersMinChars: getLineNumbersMinChars( contents.original, contents.modified, diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx index e6d852f2dd2..7946b1ccc68 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx @@ -4,9 +4,9 @@ import { type MutableRefObject, useCallback, useEffect, useRef } from "react"; import { LuLoader } from "react-icons/lu"; import { MarkdownRenderer } from "renderer/components/MarkdownRenderer"; import { - MONACO_EDITOR_OPTIONS, registerSaveAction, SUPERSET_THEME, + useMonacoEditorOptions, useMonacoReady, } from "renderer/providers/MonacoProvider"; import type { Tab } from "renderer/stores/tabs/types"; @@ -124,6 +124,7 @@ export function FileViewerContent({ }: FileViewerContentProps) { const isImage = isImageFile(filePath); const isMonacoReady = useMonacoReady(); + const monacoEditorOptions = useMonacoEditorOptions(); const hasAppliedInitialLocationRef = useRef(false); // biome-ignore lint/correctness/useExhaustiveDependencies: Reset on file change only @@ -346,7 +347,7 @@ export function FileViewerContent({
} options={{ - ...MONACO_EDITOR_OPTIONS, + ...monacoEditorOptions, contextmenu: false, // Disable Monaco's native context menu to use our custom one }} /> diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx index c56d766da75..5961cf81146 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx @@ -7,6 +7,10 @@ import { electronTrpc } from "renderer/lib/electron-trpc"; import { useTabsStore } from "renderer/stores/tabs/store"; import { useTerminalTheme } from "renderer/stores/theme"; import { ConnectionErrorOverlay, SessionKilledOverlay } from "./components"; +import { + DEFAULT_TERMINAL_FONT_FAMILY, + DEFAULT_TERMINAL_FONT_SIZE, +} from "./config"; import { getDefaultTerminalBg, type TerminalRendererRef } from "./helpers"; import { useFileLinkClick, @@ -303,6 +307,24 @@ export const Terminal = ({ paneId, tabId, workspaceId }: TerminalProps) => { xterm.options.theme = terminalTheme; }, [terminalTheme]); + const { data: fontSettings } = electronTrpc.settings.getFontSettings.useQuery( + undefined, + { + staleTime: 30_000, + }, + ); + + useEffect(() => { + const xterm = xtermRef.current; + if (!xterm || !fontSettings) return; + const family = + fontSettings.terminalFontFamily || DEFAULT_TERMINAL_FONT_FAMILY; + const size = fontSettings.terminalFontSize ?? DEFAULT_TERMINAL_FONT_SIZE; + xterm.options.fontFamily = family; + xterm.options.fontSize = size; + fitAddonRef.current?.fit(); + }, [fontSettings]); + const terminalBg = terminalTheme?.background ?? getDefaultTerminalBg(); const handleDragOver = (event: React.DragEvent) => { diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/config.ts b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/config.ts index 33bb8cdc195..a9fed0033fe 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/config.ts +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/config.ts @@ -13,7 +13,7 @@ export const DEBUG_TERMINAL = localStorage.getItem("SUPERSET_TERMINAL_DEBUG") === "1"; // Nerd Fonts first for shell theme compatibility (Oh My Posh, Powerlevel10k, etc.) -const TERMINAL_FONT_FAMILY = [ +export const DEFAULT_TERMINAL_FONT_FAMILY = [ "MesloLGM Nerd Font", "MesloLGM NF", "MesloLGS NF", @@ -31,10 +31,12 @@ const TERMINAL_FONT_FAMILY = [ "monospace", ].join(", "); +export const DEFAULT_TERMINAL_FONT_SIZE = 14; + export const TERMINAL_OPTIONS: ITerminalOptions = { cursorBlink: true, - fontSize: 14, - fontFamily: TERMINAL_FONT_FAMILY, + fontSize: DEFAULT_TERMINAL_FONT_SIZE, + fontFamily: DEFAULT_TERMINAL_FONT_FAMILY, theme: TERMINAL_THEME, allowProposedApi: true, scrollback: 10000, diff --git a/packages/local-db/drizzle/0022_add_font_settings.sql b/packages/local-db/drizzle/0022_add_font_settings.sql new file mode 100644 index 00000000000..86d3f273cc5 --- /dev/null +++ b/packages/local-db/drizzle/0022_add_font_settings.sql @@ -0,0 +1,4 @@ +ALTER TABLE `settings` ADD `terminal_font_family` text;--> statement-breakpoint +ALTER TABLE `settings` ADD `terminal_font_size` integer;--> statement-breakpoint +ALTER TABLE `settings` ADD `editor_font_family` text;--> statement-breakpoint +ALTER TABLE `settings` ADD `editor_font_size` integer; diff --git a/packages/local-db/drizzle/meta/0022_snapshot.json b/packages/local-db/drizzle/meta/0022_snapshot.json new file mode 100644 index 00000000000..21baf7ff615 --- /dev/null +++ b/packages/local-db/drizzle/meta/0022_snapshot.json @@ -0,0 +1,1113 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "6cbb83fb-b7c0-46fa-830a-2c63f67bc147", + "prevId": "8a53a71d-3904-41e1-8c3f-b0322d375a78", + "tables": { + "organization_members": { + "name": "organization_members", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organization_members_organization_id_idx": { + "name": "organization_members_organization_id_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "organization_members_user_id_idx": { + "name": "organization_members_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "organization_members_organization_id_organizations_id_fk": { + "name": "organization_members_organization_id_organizations_id_fk", + "tableFrom": "organization_members", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "organization_members_user_id_users_id_fk": { + "name": "organization_members_user_id_users_id_fk", + "tableFrom": "organization_members", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organizations": { + "name": "organizations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "clerk_org_id": { + "name": "clerk_org_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "github_org": { + "name": "github_org", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "organizations_clerk_org_id_unique": { + "name": "organizations_clerk_org_id_unique", + "columns": [ + "clerk_org_id" + ], + "isUnique": true + }, + "organizations_slug_unique": { + "name": "organizations_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "organizations_slug_idx": { + "name": "organizations_slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "organizations_clerk_org_id_idx": { + "name": "organizations_clerk_org_id_idx", + "columns": [ + "clerk_org_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "projects": { + "name": "projects", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "main_repo_path": { + "name": "main_repo_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_opened_at": { + "name": "last_opened_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "config_toast_dismissed": { + "name": "config_toast_dismissed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_branch": { + "name": "default_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_owner": { + "name": "github_owner", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_mode": { + "name": "branch_prefix_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_custom": { + "name": "branch_prefix_custom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "hide_image": { + "name": "hide_image", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon_url": { + "name": "icon_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "projects_main_repo_path_idx": { + "name": "projects_main_repo_path_idx", + "columns": [ + "main_repo_path" + ], + "isUnique": false + }, + "projects_last_opened_at_idx": { + "name": "projects_last_opened_at_idx", + "columns": [ + "last_opened_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "settings": { + "name": "settings", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "last_active_workspace_id": { + "name": "last_active_workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_used_app": { + "name": "last_used_app", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets": { + "name": "terminal_presets", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_presets_initialized": { + "name": "terminal_presets_initialized", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "selected_ringtone_id": { + "name": "selected_ringtone_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirm_on_quit": { + "name": "confirm_on_quit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_link_behavior": { + "name": "terminal_link_behavior", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "persist_terminal": { + "name": "persist_terminal", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "auto_apply_default_preset": { + "name": "auto_apply_default_preset", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_mode": { + "name": "branch_prefix_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch_prefix_custom": { + "name": "branch_prefix_custom", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_sounds_muted": { + "name": "notification_sounds_muted", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delete_local_branch": { + "name": "delete_local_branch", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "file_open_mode": { + "name": "file_open_mode", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_font_family": { + "name": "terminal_font_family", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "terminal_font_size": { + "name": "terminal_font_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "editor_font_family": { + "name": "editor_font_family", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "editor_font_size": { + "name": "editor_font_size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "tasks": { + "name": "tasks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_color": { + "name": "status_color", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_type": { + "name": "status_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_position": { + "name": "status_position", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "repository_id": { + "name": "repository_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assignee_id": { + "name": "assignee_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "estimate": { + "name": "estimate", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "due_date": { + "name": "due_date", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "labels": { + "name": "labels", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pr_url": { + "name": "pr_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_provider": { + "name": "external_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_key": { + "name": "external_key", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_url": { + "name": "external_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_synced_at": { + "name": "last_synced_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sync_error": { + "name": "sync_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "completed_at": { + "name": "completed_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "tasks_slug_unique": { + "name": "tasks_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "tasks_slug_idx": { + "name": "tasks_slug_idx", + "columns": [ + "slug" + ], + "isUnique": false + }, + "tasks_organization_id_idx": { + "name": "tasks_organization_id_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "tasks_assignee_id_idx": { + "name": "tasks_assignee_id_idx", + "columns": [ + "assignee_id" + ], + "isUnique": false + }, + "tasks_status_idx": { + "name": "tasks_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "tasks_created_at_idx": { + "name": "tasks_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "tasks_organization_id_organizations_id_fk": { + "name": "tasks_organization_id_organizations_id_fk", + "tableFrom": "tasks", + "tableTo": "organizations", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tasks_assignee_id_users_id_fk": { + "name": "tasks_assignee_id_users_id_fk", + "tableFrom": "tasks", + "tableTo": "users", + "columnsFrom": [ + "assignee_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "tasks_creator_id_users_id_fk": { + "name": "tasks_creator_id_users_id_fk", + "tableFrom": "tasks", + "tableTo": "users", + "columnsFrom": [ + "creator_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "users": { + "name": "users", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "clerk_id": { + "name": "clerk_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "columns": [ + "clerk_id" + ], + "isUnique": true + }, + "users_email_unique": { + "name": "users_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + "email" + ], + "isUnique": false + }, + "users_clerk_id_idx": { + "name": "users_clerk_id_idx", + "columns": [ + "clerk_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspaces": { + "name": "workspaces", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "worktree_id": { + "name": "worktree_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "tab_order": { + "name": "tab_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_opened_at": { + "name": "last_opened_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_unread": { + "name": "is_unread", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "is_unnamed": { + "name": "is_unnamed", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "deleting_at": { + "name": "deleting_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "workspaces_project_id_idx": { + "name": "workspaces_project_id_idx", + "columns": [ + "project_id" + ], + "isUnique": false + }, + "workspaces_worktree_id_idx": { + "name": "workspaces_worktree_id_idx", + "columns": [ + "worktree_id" + ], + "isUnique": false + }, + "workspaces_last_opened_at_idx": { + "name": "workspaces_last_opened_at_idx", + "columns": [ + "last_opened_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspaces_project_id_projects_id_fk": { + "name": "workspaces_project_id_projects_id_fk", + "tableFrom": "workspaces", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspaces_worktree_id_worktrees_id_fk": { + "name": "workspaces_worktree_id_worktrees_id_fk", + "tableFrom": "workspaces", + "tableTo": "worktrees", + "columnsFrom": [ + "worktree_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "worktrees": { + "name": "worktrees", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "project_id": { + "name": "project_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "base_branch": { + "name": "base_branch", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "git_status": { + "name": "git_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "github_status": { + "name": "github_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "worktrees_project_id_idx": { + "name": "worktrees_project_id_idx", + "columns": [ + "project_id" + ], + "isUnique": false + }, + "worktrees_branch_idx": { + "name": "worktrees_branch_idx", + "columns": [ + "branch" + ], + "isUnique": false + } + }, + "foreignKeys": { + "worktrees_project_id_projects_id_fk": { + "name": "worktrees_project_id_projects_id_fk", + "tableFrom": "worktrees", + "tableTo": "projects", + "columnsFrom": [ + "project_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} diff --git a/packages/local-db/drizzle/meta/_journal.json b/packages/local-db/drizzle/meta/_journal.json index b2454c0233f..89ee3c96c1d 100644 --- a/packages/local-db/drizzle/meta/_journal.json +++ b/packages/local-db/drizzle/meta/_journal.json @@ -155,6 +155,13 @@ "when": 1770783531923, "tag": "0021_add_image_project", "breakpoints": true + }, + { + "idx": 22, + "version": "6", + "when": 1770823982765, + "tag": "0022_add_font_settings", + "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/local-db/src/schema/schema.ts b/packages/local-db/src/schema/schema.ts index 73137d2d064..04e47d73864 100644 --- a/packages/local-db/src/schema/schema.ts +++ b/packages/local-db/src/schema/schema.ts @@ -160,6 +160,10 @@ export const settings = sqliteTable("settings", { }), deleteLocalBranch: integer("delete_local_branch", { mode: "boolean" }), fileOpenMode: text("file_open_mode").$type(), + terminalFontFamily: text("terminal_font_family"), + terminalFontSize: integer("terminal_font_size"), + editorFontFamily: text("editor_font_family"), + editorFontSize: integer("editor_font_size"), }); export type InsertSettings = typeof settings.$inferInsert;