feat(desktop): custom font settings for terminal and editor#1336
feat(desktop): custom font settings for terminal and editor#1336glitch418x wants to merge 8 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements custom font settings by adding TRPC procedures to manage editor and terminal fonts, extending the database schema with four new columns, creating a settings UI with font preview and configuration, and integrating dynamic font options throughout the editor and terminal components. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as Settings UI
participant Hook as useMonacoEditorOptions<br/>(Editor/Terminal)
participant TRPC as TRPC Router
participant DB as Database
User->>UI: Configure font settings
UI->>TRPC: setFontSettings(fontData)
TRPC->>DB: Upsert font columns
DB-->>TRPC: Success
TRPC-->>UI: Return result
UI->>UI: Optimistic update + invalidate
Hook->>TRPC: getFontSettings.useQuery()
TRPC->>DB: SELECT font columns
DB-->>TRPC: Return font settings
TRPC-->>Hook: fontSettings data
Hook->>Hook: Memoize merged options<br/>(fallback to defaults)
Hook-->>User: Applied fonts in editor/terminal
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @.claude-task-prompt.md:
- Around line 1-78: Remove the accidental .claude-task-prompt.md commit: delete
the file from the branch (git rm .claude-task-prompt.md), commit with a
conventional message (e.g., chore: remove AI task prompt file), and push to the
fork; if the file was included in an earlier commit that must be removed,
interactively rebase/amend to eliminate it or use git revert to undo that
addition before pushing. Also ensure the Appearance/Font implementation changes
are kept separate from this cleanup and add an entry to .gitignore if needed to
prevent future accidental commits of agent prompt files.
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx`:
- Around line 254-264: The Input uses defaultValue (uncontrolled) so it doesn't
update when fontSettings loads; change it to a controlled input by setting its
value to the draft or the loaded setting (e.g., value={editorFontDraft ??
fontSettings?.editorFontFamily ?? ""}), keep onChange={(e) =>
setEditorFontDraft(e.target.value)} and keep the onBlur handler
(handleEditorFontFamilyBlur and setEditorFontDraft(null)) as-is; make the
identical change for the terminal font family Input (the other Input handling
terminal font family) so both inputs reflect server-loaded fontSettings after
mount.
- Around line 30-35: The file defines duplicated DEFAULT_EDITOR_FONT_FAMILY,
DEFAULT_EDITOR_FONT_SIZE, DEFAULT_TERMINAL_FONT_FAMILY, and
DEFAULT_TERMINAL_FONT_SIZE which drift from the canonical values; update the
code to import the editor and terminal defaults from the single source of truth
instead of hardcoding them—specifically replace local
DEFAULT_EDITOR_FONT_FAMILY/DEFAULT_EDITOR_FONT_SIZE with the values exported by
MONACO_EDITOR_OPTIONS (from MonacoProvider.tsx) and replace
DEFAULT_TERMINAL_FONT_FAMILY/DEFAULT_TERMINAL_FONT_SIZE with the exports from
Terminal/config.ts so the component reads the shared constants rather than
duplicating them.
🧹 Nitpick comments (2)
apps/desktop/src/renderer/providers/MonacoProvider/MonacoProvider.tsx (1)
131-151: Clean hook implementation with proper memoization.One subtlety: when a custom
fontSizeis applied (e.g., 20+), thelineHeight: 20fromMONACO_EDITOR_OPTIONS(Line 113) remains unchanged, which may cause cramped rendering at larger sizes. Consider also overridinglineHeightproportionally, or omitting it from the base options so Monaco can auto-calculate it.apps/desktop/src/lib/trpc/routers/settings/index.ts (1)
486-533: Consider guarding against no-op mutations and adding a max length to font family strings.Two minor points:
If all input fields are
undefined, the mutation still executes an upsert with an emptysetobject — a harmless but unnecessary DB write. A short-circuit (if (Object.keys(set).length === 0) return { success: true }) would avoid this.
z.string().nullable().optional()for font families has no max length, so a caller could persist an arbitrarily long string. Consider adding.max(500)or similar as a safety bound.Proposed changes
.input( z.object({ - terminalFontFamily: z.string().nullable().optional(), + terminalFontFamily: z.string().max(500).nullable().optional(), terminalFontSize: z .number() .int() .min(10) .max(24) .nullable() .optional(), - editorFontFamily: z.string().nullable().optional(), + editorFontFamily: z.string().max(500).nullable().optional(), editorFontSize: z .number() .int() .min(10) .max(24) .nullable() .optional(), }), ) .mutation(({ input }) => { const set: Record<string, string | number | null> = {}; 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; } + if (Object.keys(set).length === 0) { + return { success: true }; + } + localDb
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/desktop/src/lib/trpc/routers/settings/font-settings.test.ts`:
- Around line 12-40: Tests duplicate the Zod schema and transform logic
(setFontSettingsSchema and transformFontSettings) from the router which can
drift; fix this by extracting/exporting the canonical definitions from the
router (or a new shared module like font-settings.utils) and update the test to
import setFontSettingsSchema and transformFontSettings from that module so the
tests exercise the real code rather than a copy.
🧹 Nitpick comments (1)
apps/desktop/src/lib/trpc/routers/settings/font-settings.test.ts (1)
43-57: Defaults test asserts a hardcoded literal, not production code.This test constructs an object inline and asserts its own literal values are null — it doesn't exercise any application code path. It will always pass regardless of what
getFontSettingsactually returns.Either import and call the real
getFontSettings(with a mocked/empty DB) or remove this test to avoid false confidence.
|
hello @Kitenite would you mind having a look at this? |
|
Very cool will look! |
e47d3a4 to
5307d10
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx`:
- Around line 266-274: The font-size Input is fully controlled from fontSettings
so intermediate keystrokes are rejected by handleEditorFontSizeChange; add a
local draft state (e.g., editorFontSizeDraft) initialized from
fontSettings?.editorFontSize ?? DEFAULT_EDITOR_FONT_SIZE, bind Input value to
that draft, update the draft on every onChange without immediate validation, and
only validate and call the existing mutate inside onBlur or onSubmit (or on
Enter) using the same 10–24 range; also sync the draft when fontSettings
changes. Repeat the same pattern for the terminal font size
(terminalFontSizeDraft and its onChange/onBlur handler) so typing “2” won’t snap
back until the value is committed.
In `@packages/local-db/drizzle/meta/0021_snapshot.json`:
- Line 4: The snapshot ID "a1b2c3d4-font-sett-ings-snapshot000021" is manually
edited and not a valid UUID v4; revert the manual change and regenerate this
snapshot using the drizzle CLI (run drizzle-kit generate) so Drizzle produces a
proper UUID v4 ID and valid contents for the snapshot file, then commit the
regenerated file and avoid manual edits to auto-generated Drizzle snapshots in
the future.
🧹 Nitpick comments (2)
apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.test.ts (1)
53-64: Consider adding explicit existence assertions before checking properties.If
find()returnsundefined, the test still fails but with a confusing message likeexpected undefined to be "appearance". Addingexpect(editorFont).toBeDefined()before the section check would produce a clearer failure.Suggested improvement
expect(editorFont?.section).toBe("appearance"); + // or, for clearer failure messages: + // expect(editorFont).toBeDefined(); + // expect(editorFont!.section).toBe("appearance"); expect(terminalFont?.section).toBe("appearance");apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx (1)
41-66: Hardcoded terminal preview colors won't follow the active theme.
bg-[#1e1e1e] text-[#cccccc] border-[#333]will look out of place on light or non-dark themes. Consider using semantic Tailwind classes (e.g.,bg-background text-foreground border-border) or reading the active terminal theme colors so the preview stays consistent.
| <Input | ||
| type="number" | ||
| min={10} | ||
| max={24} | ||
| value={fontSettings?.editorFontSize ?? DEFAULT_EDITOR_FONT_SIZE} | ||
| onChange={handleEditorFontSizeChange} | ||
| disabled={isFontLoading} | ||
| className="w-20" | ||
| /> |
There was a problem hiding this comment.
Font size input lacks draft state — typing a new value snaps back immediately.
The font size <Input> is controlled via value={fontSettings?.editorFontSize ?? DEFAULT_EDITOR_FONT_SIZE}, but handleEditorFontSizeChange only calls mutate when the parsed value falls within 10–24. If a user selects all and types "2" (intending to continue typing "20"), the value is rejected and React immediately resets the display to the old value, making it feel unresponsive.
Consider adding a local draft state for font size (like you do for font family) and committing on blur, or at least debouncing the mutation so intermediate keystrokes aren't lost.
The same applies to the terminal font size input (Lines 336-346).
🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/AppearanceSettings.tsx`
around lines 266 - 274, The font-size Input is fully controlled from
fontSettings so intermediate keystrokes are rejected by
handleEditorFontSizeChange; add a local draft state (e.g., editorFontSizeDraft)
initialized from fontSettings?.editorFontSize ?? DEFAULT_EDITOR_FONT_SIZE, bind
Input value to that draft, update the draft on every onChange without immediate
validation, and only validate and call the existing mutate inside onBlur or
onSubmit (or on Enter) using the same 10–24 range; also sync the draft when
fontSettings changes. Repeat the same pattern for the terminal font size
(terminalFontSizeDraft and its onChange/onBlur handler) so typing “2” won’t snap
back until the value is committed.
| { | ||
| "version": "6", | ||
| "dialect": "sqlite", | ||
| "id": "a1b2c3d4-font-sett-ings-snapshot000021", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check all snapshot IDs in the local-db drizzle directory
echo "=== All snapshot IDs in packages/local-db/drizzle/meta/ ==="
fd 'snapshot.json' packages/local-db/drizzle/meta/ --exec grep '"id"' {} + 2>/dev/null | head -20
echo ""
echo "=== Listing all snapshot files ==="
fd 'snapshot.json' packages/local-db/drizzle/meta/ -x ls -1 2>/dev/nullRepository: superset-sh/superset
Length of output: 2672
🏁 Script executed:
#!/bin/bash
# Extract just the top-level snapshot ID from each file (first "id" field)
echo "=== Top-level snapshot IDs ==="
for file in packages/local-db/drizzle/meta/*_snapshot.json; do
filename=$(basename "$file")
id=$(head -10 "$file" | grep -m1 '"id"' | sed 's/.*"id": "\([^"]*\)".*/\1/')
echo "$filename: $id"
done | sortRepository: superset-sh/superset
Length of output: 1350
Regenerate this snapshot—it was manually edited.
The snapshot ID "a1b2c3d4-font-sett-ings-snapshot000021" is not a Drizzle-generated UUID. All legitimate snapshots in this directory use proper UUID v4 format (e.g., 2c6f4b00-72ca-4cc3-bc0a-f25a40163119). This ID explicitly spells out readable text (font-sett-ings-snapshot), indicating manual editing.
Do not manually edit auto-generated Drizzle files. Regenerate this snapshot using drizzle-kit generate to ensure drizzle-kit operations (push, migrate, etc.) function correctly.
🤖 Prompt for AI Agents
In `@packages/local-db/drizzle/meta/0021_snapshot.json` at line 4, The snapshot ID
"a1b2c3d4-font-sett-ings-snapshot000021" is manually edited and not a valid UUID
v4; revert the manual change and regenerate this snapshot using the drizzle CLI
(run drizzle-kit generate) so Drizzle produces a proper UUID v4 ID and valid
contents for the snapshot file, then commit the regenerated file and avoid
manual edits to auto-generated Drizzle snapshots in the future.
Add configurable font family and font size settings for both terminal panels and editor/diff views in the Appearance settings page. - Add terminal_font_family, terminal_font_size, editor_font_family, editor_font_size columns to local-db settings table - Add getFontSettings/setFontSettings tRPC procedures - Add Editor Font and Terminal Font sections to Appearance settings with text input for font family, number input for size (10-24), and reset to default button - Wire up Terminal to apply custom fonts via xterm options with live updates - Wire up Monaco editor/diff views via useMonacoEditorOptions hook - Add settings search entries for font customization
…ts and live preview
- Move Zod schema and transform logic to font-settings.utils.ts (single source of truth) and import in both the router and test file - Add .max(500) length limit on font family strings for safety - Add no-op guard to skip DB write when no fields are provided - Remove ineffective defaults test that only asserted its own literals Addresses remaining CodeRabbit review feedback on PR #1336. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add draft state for font size inputs (editor & terminal) so mutations only fire on blur instead of every keystroke, matching the existing font family draft pattern - Replace fake snapshot UUID in migration 0021 with a real UUID v4
7028cb1 to
494642d
Compare
|
Hey @uinafdev! I've built on top of your work here with a refactor of the The main changes:
This brings |
|
This is sick and sorry I missed merging this in! @glitch418x |
|
Either way this is awesome. Thanks so much! |
…#1431) * feat(desktop): add custom font settings for terminal and editor Add configurable font family and font size settings for both terminal panels and editor/diff views in the Appearance settings page. - Add terminal_font_family, terminal_font_size, editor_font_family, editor_font_size columns to local-db settings table - Add getFontSettings/setFontSettings tRPC procedures - Add Editor Font and Terminal Font sections to Appearance settings with text input for font family, number input for size (10-24), and reset to default button - Wire up Terminal to apply custom fonts via xterm options with live updates - Wire up Monaco editor/diff views via useMonacoEditorOptions hook - Add settings search entries for font customization * feat(appearance): improve font settings layout with side-by-side inputs and live preview * feat(appearance): make Nerd Fonts a hyperlink in terminal font description * chore: remove claude task prompt * test(desktop): add tests for custom font settings * fix(appearance): address CodeRabbit review feedback on font settings * refactor(settings): extract font settings schema into shared module - Move Zod schema and transform logic to font-settings.utils.ts (single source of truth) and import in both the router and test file - Add .max(500) length limit on font family strings for safety - Add no-op guard to skip DB write when no fields are provided - Remove ineffective defaults test that only asserted its own literals Addresses remaining CodeRabbit review feedback on PR #1336. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit review feedback on font settings - Add draft state for font size inputs (editor & terminal) so mutations only fire on blur instead of every keystroke, matching the existing font family draft pattern - Replace fake snapshot UUID in migration 0021 with a real UUID v4 * fix: regenerate font settings migration after merge with main * chore: remove font settings migration for regeneration * refactor(desktop): extract AppearanceSettings into section components Break the monolithic 432-line AppearanceSettings into focused section components following the established TerminalSettings pattern: - FontPreview: extracted inline preview component - FontSettingSection: reusable editor/terminal font section (deduplicates ~120 lines) - ThemeSection: owns theme store hooks and grid rendering - MarkdownStyleSection: owns markdown store hooks and select dropdown - CustomThemesSection: placeholder for upcoming feature Use SectionList for automatic border separators, replacing fragile conditional className logic. --------- Co-authored-by: glitch418x <glitch@uinaf.dev>

Custom Font Settings for Terminal and Editor
Adds user-configurable font settings in Settings > Appearance:
Screenshot
Implementation
terminal_font_family,terminal_font_size,editor_font_family,editor_font_size) + Drizzle migrationgetFontSettings/setFontSettingswith validation (size clamped 10-24)fitAddon.fit()useMonacoEditorOptionshook merging custom fonts into defaultsCloses #751
Summary by CodeRabbit
New Features