Preference Schema Guide

May 29, 2026 ยท View on GitHub

This guide explains how to add new preference keys to Cherry Studio.

Key Naming Conventions

Format

All preference keys MUST follow the format: namespace.sub.key_name

Rules:

  • At least 2 segments separated by dots (.)
  • Each segment uses lowercase letters, numbers, and underscores only
  • Pattern: /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/

Naming Principles

  1. Semantic Grouping: Group related settings under common namespaces

    • app.* - Application-level settings
    • chat.* - Chat/message settings
    • feature.* - Feature toggles
    • ui.* - UI/theme settings
    • data.* - Data/backup settings
    • shortcut.* - Keyboard shortcuts

    Namespace principles:

    • Namespaces represent major features with global impact across the application
    • The existing namespaces should already cover most use cases
    • If you believe a new namespace is needed, think from a global perspective - it should represent a fundamental category, not just a single feature
  2. Hierarchy: Use dots for hierarchy, underscores for multi-word names

    • chat.message.font_size (not chat.messageFontSize)
    • feature.quick_assistant.enabled (not feature.quickAssistant.enabled)
  3. Boolean Naming: Use positive names with .enabled suffix for toggles

    • feature.quick_assistant.enabled (not feature.quick_assistant.disabled)
    • app.spell_check.enabled

Examples

ValidInvalidReason
app.user.avataruserAvatarMissing dot separator
chat.multi_select_modechat.multiSelectModecamelCase not allowed
feature.quick_assistant.enabledFeature.quickAssistantcamelCase not allowed

Design Principles

Prefer Flat Over Nested

Prefer granular, flat preference keys over storing complex objects.

Why:

  1. Visibility: Individual config items are more explicit and discoverable
  2. Performance: Avoids parsing entire objects when reading/writing common items

When to use flat keys:

// Good: Flat keys for independent settings
'chat.code.collapsible': boolean
'chat.code.show_line_numbers': boolean
'chat.code.wrappable': boolean

When to keep as object:

Only use object values when the data is frequently read/written as a whole unit.

// Acceptable: Shortcut config is always read/written together
'shortcut.general.show_main_window': { binding: string[], enabled: boolean }

Rule of thumb: If you find yourself frequently accessing just one property of an object, split it into separate keys.

Keep Values Atomic

Each preference should represent one logical setting. Don't combine unrelated settings.

// Good: One setting per key
'chat.message.font_size': number
'chat.message.font_family': string

// Bad: Multiple settings in one key
'chat.message.font': { size: number, family: string }

Provide Sensible Defaults

All preferences MUST have default values in DefaultPreferences.

Adding a New Preference

Step 1: Define Custom Types (if needed)

If your preference uses a custom type (enum, union type, etc.), add it first.

File: src/shared/data/preference/preferenceTypes.ts

// Example: Adding a new enum type
export enum MyFeatureMode {
  auto = 'auto',
  manual = 'manual',
  disabled = 'disabled'
}

Step 2: Add to Schema Interface

File: src/shared/data/preference/preferenceSchemas.ts

Add your key to the PreferenceSchemas interface:

export interface PreferenceSchemas {
  default: {
    // ...existing keys (alphabetically sorted)...
    'feature.my_feature.enabled': boolean
    'feature.my_feature.mode': PreferenceTypes.MyFeatureMode
  }
}

Step 3: Add Default Value

In the same file, add default value to DefaultPreferences:

export const DefaultPreferences: PreferenceSchemas = {
  default: {
    // ...existing defaults (alphabetically sorted)...
    'feature.my_feature.enabled': true,
    'feature.my_feature.mode': PreferenceTypes.MyFeatureMode.auto,
  }
}

Step 4: Use in Code

import { usePreference } from '@data/hooks/usePreference'

const [enabled, setEnabled] = usePreference('feature.my_feature.enabled')
const [mode, setMode] = usePreference('feature.my_feature.mode')

File Structure

FilePurpose
src/shared/data/preference/preferenceSchemas.tsSchema interface and default values
src/shared/data/preference/preferenceTypes.tsCustom type definitions (enums, unions)

Best Practices Summary

  1. Flat over nested: Split objects into individual keys unless frequently accessed as a whole
  2. Atomic values: One preference = one logical setting
  3. Sensible defaults: All preferences must have default values
  4. Consistent naming: Follow namespace.category.key_name pattern
  5. 2-3 levels: Don't over-nest; 2-3 dot-separated segments is usually sufficient