Preference System Overview

July 17, 2026 · View on GitHub

The Preference system provides centralized management for user configuration and application settings with cross-window synchronization.

Purpose

PreferenceService handles data that:

  • Is a user-modifiable setting that affects app behavior
  • Has a fixed key structure with stable value types
  • Needs to persist permanently until explicitly changed
  • Should sync automatically across all application windows

Key Characteristics

Fixed Key Structure

  • Predefined keys in the schema (users modify values, not keys)
  • Supports 158 configuration items
  • Nested key paths supported (e.g., app.theme.mode)

Atomic Values

  • Each preference item represents one logical setting
  • Values are typically: boolean, string, number, or simple array/object
  • Changes are independent (updating one doesn't affect others)

Cross-Window Synchronization

  • Changes automatically broadcast to all windows
  • Consistent state across main window, quick assistant, etc.
  • Conflict resolution handled by Main process

Update Strategies

Optimistic Updates (Default)

// UI updates immediately, then syncs to database
await preferenceService.set("app.theme.mode", "dark");
  • Best for: frequent, non-critical settings
  • Behavior: Local state updates first, then persists
  • Rollback: Automatic revert if persistence fails

Pessimistic Updates

// Waits for database confirmation before updating UI
await preferenceService.set("api.key", "secret", { optimistic: false });
  • Best for: critical settings (API keys, security options)
  • Behavior: Persists first, then updates local state
  • No rollback needed: UI only updates on success

Architecture Diagram

┌─────────────────────────────────────────────────────┐
│ Renderer Process                                    │
│ ┌─────────────────────────────────────────────────┐ │
│ │ usePreference Hook                              │ │
│ │ - Subscribe to preference changes               │ │
│ │ - Optimistic/pessimistic update support         │ │
│ └──────────────────────┬──────────────────────────┘ │
│                        ▼                            │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PreferenceService (Renderer)                    │ │
│ │ - Local cache for fast reads                    │ │
│ │ - IPC proxy to Main process                     │ │
│ │ - Subscription management                       │ │
│ └──────────────────────┬──────────────────────────┘ │
└────────────────────────┼────────────────────────────┘
                         │ IPC
┌────────────────────────┼────────────────────────────┐
│ Main Process           ▼                            │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PreferenceService (Main)                        │ │
│ │ - Full memory cache of all preferences          │ │
│ │ - SQLite persistence via Drizzle ORM            │ │
│ │ - Cross-window broadcast                        │ │
│ └──────────────────────┬──────────────────────────┘ │
│                        ▼                            │
│ ┌─────────────────────────────────────────────────┐ │
│ │ SQLite Database (preference table)              │ │
│ │ - scope + key structure                         │ │
│ │ - JSON value storage                            │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘

All Preference_* IPC channels are sender-gated by validateSender (untrusted senders are rejected) — see IpcApi Overview §Security.

Main vs Renderer Responsibilities

Main Process PreferenceService

  • Source of truth for all preferences
  • Full memory cache for fast access
  • SQLite persistence via preference table
  • Broadcasts changes to all renderer windows
  • Handles batch operations and transactions

Renderer Process PreferenceService

  • Local cache for read performance
  • Proxies write operations to Main
  • Manages React hook subscriptions
  • Handles optimistic update rollbacks
  • Listens for cross-window updates

Statistics (Debug)

Main process provides getStats(details?) for debugging subscription status:

  • Returns total keys, main process subscriptions, and window subscriptions
  • Pass details=true for per-key breakdown
  • Warning: Resource-intensive, recommended for development only
import { application } from '@application'

const preferenceService = application.get('PreferenceService')
const stats = preferenceService.getStats(true);

Database Schema

Preferences are stored in the preference table:

// Simplified schema
{
  scope: string; // e.g., 'default', 'user'
  key: string; // e.g., 'app.theme.mode'
  value: json; // The preference value
  createdAt: number;
  updatedAt: number;
}

Preference Categories

Application Settings

  • Theme mode, language, font sizes
  • Window behavior, startup options

Feature Toggles

  • Show/hide UI elements
  • Enable/disable features

User Customization

  • Keyboard shortcuts
  • Default values for operations

Provider Configuration

  • AI provider settings
  • API endpoints and tokens

Usage Summary

For detailed code examples and API usage, see Preference Usage Guide.

OperationHookService Method
Read singleusePreference(key)preferenceService.get(key)
Write singlesetPreference(value)preferenceService.set(key, value)
Read multipleusePreferences([...keys])preferenceService.getMultiple([...keys])
Write multiple-preferenceService.setMultiple({...})