Skip to content

Improve UI font handling on Linux#167

Merged
dannysmith merged 3 commits intomainfrom
linux-ui-font-detection
Mar 28, 2026
Merged

Improve UI font handling on Linux#167
dannysmith merged 3 commits intomainfrom
linux-ui-font-detection

Conversation

@dannysmith
Copy link
Copy Markdown
Owner

@dannysmith dannysmith commented Mar 28, 2026

Summary

  • Detect the Linux desktop environment's configured UI font at startup via gsettings (GNOME/Cinnamon/Budgie/XFCE) and ~/.config/kdeglobals (KDE Plasma), applied as a CSS variable
  • Expand the static CSS font-family fallback stack to include common Linux UI fonts (Ubuntu, Cantarell, Noto Sans, DejaVu Sans) so the UI renders well even when detection fails
  • No change on macOS or Windows — the existing -apple-system / Segoe UI behaviour is preserved

How it works

  1. CSS fallback stack (App.css): font-family now uses var(--font-ui, ...) with an improved fallback that covers the major Linux distros
  2. Rust command (fonts.rs): get_linux_ui_font() tries gsettings first, then KDE config. Returns None on non-Linux (compiled out via #[cfg])
  3. Frontend (Layout.tsx): Calls the command once on startup if running on Linux, sets --font-ui CSS variable with the detected font prepended to the full fallback stack

Why not system-ui?

Tauri uses WebKitGTK on Linux, where CSS system-ui is unreliable. Explicit detection is more dependable.

Closes #160

Summary by CodeRabbit

  • New Features
    • Linux: The app now auto-detects your system UI font and applies it globally for a more native look, with sensible fallbacks if detection fails. The change runs only on Linux and won’t affect other platforms.

dannysmith and others added 2 commits March 27, 2026 23:58
Detect the desktop environment's configured UI font on Linux via
gsettings (GNOME/Cinnamon/Budgie/XFCE) and kdeglobals (KDE), and apply
it as a CSS variable. Also expand the static fallback font stack to
include common Linux UI fonts (Ubuntu, Cantarell, Noto Sans, DejaVu
Sans) so the UI renders well even when detection fails.

Closes #160

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: df726a02-d3df-4840-87ee-770f29297fe0

📥 Commits

Reviewing files that changed from the base of the PR and between f96c478 and 88eae89.

📒 Files selected for processing (1)
  • src/components/layout/Layout.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/layout/Layout.tsx

📝 Walkthrough

Walkthrough

Adds Linux UI font detection: a new Tauri command and fonts module, Linux-specific detection/parsing logic, and frontend changes that set a CSS variable with the detected font on Linux at runtime.

Changes

Cohort / File(s) Summary
Tauri bindings & registration
src-tauri/src/bindings.rs
Registered new get_linux_ui_font command in the Tauri command collection.
Commands module export
src-tauri/src/commands/mod.rs
Added pub mod fonts; to export the new fonts command module.
Linux font detection command
src-tauri/src/commands/fonts.rs
Added pub async fn get_linux_ui_font() -> Result<Option<String>, String>: Linux-only detection via gsettings parsing and KDE ~/.config/kdeglobals fallback; includes unit tests for parsing behavior.
Frontend CSS
src/App.css
Replaced body font-family with var(--font-ui, ...) and extended fallback UI font stack.
Frontend runtime application
src/components/layout/Layout.tsx
On Linux, fetches getLinuxUiFont() on mount and sets --font-ui on document.documentElement; includes cancellation guard and error suppression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through configs, sniffed GNOME and KDE,
Found the font that makes UI faces glee.
I set a CSS variable, neat and spry,
So Linux windows dress up sly.
Cheers from a rabbit — snug, sans-serif, and wry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Improve UI font handling on Linux' directly and clearly describes the main change: implementing Linux-specific UI font detection and application via CSS variables.
Linked Issues check ✅ Passed The PR fully addresses issue #160's objective to provide a Linux-specific UI font solution by detecting desktop environment fonts (gsettings for GNOME/Cinnamon/XFCE, kdeglobals for KDE) and applying them as CSS variables with appropriate fallbacks.
Out of Scope Changes check ✅ Passed All changes are directly related to UI font handling on Linux. The Rust backend detects system fonts only on Linux, the CSS and Layout component focus on UI chrome font handling, and editor fonts remain out of scope as required.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch linux-ui-font-detection

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/components/layout/Layout.tsx (1)

190-198: Consider handling potential promise rejection.

The commands.getLinuxUiFont() call uses .then() without a .catch() handler. If the command fails at the IPC level (not just returning an error result), the promise rejection would be unhandled.

🛡️ Suggested fix to handle rejection
-    void commands.getLinuxUiFont().then(result => {
+    void commands.getLinuxUiFont().then(result => {
       if (cancelled) return
       if (result.status === 'ok' && result.data) {
         window.document.documentElement.style.setProperty(
           '--font-ui',
           `'${result.data}', -apple-system, 'Segoe UI', 'Ubuntu', 'Cantarell', 'Noto Sans', 'DejaVu Sans', sans-serif`
         )
       }
-    })
+    }).catch(() => {
+      // Font detection failed at IPC level; fallback fonts will be used
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/Layout.tsx` around lines 190 - 198, The call to
commands.getLinuxUiFont() can reject and currently has no rejection handler;
update the caller (the anonymous promise handler where cancelled is checked and
window.document.documentElement.style.setProperty is set) to handle promise
rejections — either convert to async/await inside an async function with
try/catch or append a .catch(err => { if (!cancelled) { /* log or silently
handle */ } }) to the existing chain — and ensure you still check cancelled
before touching the DOM and surface/log the error appropriately.
src-tauri/src/commands/fonts.rs (1)

34-37: Blocking process execution in async context.

Command::new(...).output() is a synchronous/blocking call within an async fn. While Tauri handles this by running commands on a separate thread pool, consider using tokio::process::Command for true async execution if this becomes a performance concern on startup.

This is acceptable for a startup-only operation but worth noting for future reference.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src-tauri/src/commands/fonts.rs` around lines 34 - 37, The current sync call
using std::process::Command::output() inside the async context should be
replaced with tokio::process::Command to avoid blocking the async runtime:
import tokio::process::Command, replace
Command::new(...).args(...).output().ok()? with an awaited async call like
Command::new(...).args(...).output().await and propagate or handle the Result
(instead of .ok()?) inside the existing async function (e.g., the function in
commands/fonts.rs that calls this), ensuring necessary use statements and error
handling are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src-tauri/src/commands/fonts.rs`:
- Around line 34-37: The current sync call using std::process::Command::output()
inside the async context should be replaced with tokio::process::Command to
avoid blocking the async runtime: import tokio::process::Command, replace
Command::new(...).args(...).output().ok()? with an awaited async call like
Command::new(...).args(...).output().await and propagate or handle the Result
(instead of .ok()?) inside the existing async function (e.g., the function in
commands/fonts.rs that calls this), ensuring necessary use statements and error
handling are updated accordingly.

In `@src/components/layout/Layout.tsx`:
- Around line 190-198: The call to commands.getLinuxUiFont() can reject and
currently has no rejection handler; update the caller (the anonymous promise
handler where cancelled is checked and
window.document.documentElement.style.setProperty is set) to handle promise
rejections — either convert to async/await inside an async function with
try/catch or append a .catch(err => { if (!cancelled) { /* log or silently
handle */ } }) to the existing chain — and ensure you still check cancelled
before touching the DOM and surface/log the error appropriately.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: d60a836b-6157-4059-809d-fbdae2df69a3

📥 Commits

Reviewing files that changed from the base of the PR and between 936b7e0 and f96c478.

⛔ Files ignored due to path filters (1)
  • src/lib/bindings.ts is excluded by !src/lib/bindings.ts
📒 Files selected for processing (5)
  • src-tauri/src/bindings.rs
  • src-tauri/src/commands/fonts.rs
  • src-tauri/src/commands/mod.rs
  • src/App.css
  • src/components/layout/Layout.tsx

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@dannysmith dannysmith merged commit 98cfb85 into main Mar 28, 2026
8 checks passed
@dannysmith dannysmith deleted the linux-ui-font-detection branch March 28, 2026 01:27
dannysmith added a commit that referenced this pull request Mar 28, 2026
* origin/main:
  Formatting issues
  Improve UI font handling on Linux (#167)
  Fix Content Linker
  Update docs and complete tasks
  Upgrade vite
  Update jsdom
  Update knip
  Upgrade lucide-react from 0.564 to 1.7
  Upgrade @ast-grep/cli from 0.41 to 0.42
  Update deps
  Upgrade deps
  Update task doc to reflect completed phases
  Upgrade notify crate from v8 to v9.0.0-rc.2
  Add file watcher error recovery and periodic rescan
  Fix macOS window close to hide instead of quit
  chore(deps): bump rustls-webpki from 0.103.9 to 0.103.10 in /src-tauri
  chore(deps): bump wrangler
  chore(deps): bump tar from 0.4.44 to 0.4.45 in /src-tauri
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider UI font customization for Linux

1 participant