Files
CherryHQ-cherry-studio/docs/references/data/preference-schema-guide.md
fullex c514dcc049 refactor(shared): move packages/shared to src/shared
packages/shared was never a real pnpm workspace package (no package.json); it was referenced only through the @shared TypeScript path alias. Relocate it under src/ via git mv (143 files, detected as pure renames).

Repoint the @shared alias and include globs to src/shared across electron.vite.config.ts, tsconfig.{json,node,web}.json and vitest.config.ts; update scripts/check-custom-exts.ts, scripts/update-languages.ts, the eslint.config.mjs generated-file globs, the data-classify generator output targets, .github/CODEOWNERS path rules, and CLAUDE.md/docs/source-comment references.

The @shared alias name is unchanged, so all 1403 @shared/* import sites resolve without modification. Verified with typecheck:node, typecheck:web and the full test suite (700 files, 9739 tests passing).
2026-05-28 21:02:49 -07:00

5.4 KiB

Preference Schema Guide

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

Valid Invalid Reason
app.user.avatar userAvatar Missing dot separator
chat.multi_select_mode chat.multiSelectMode camelCase not allowed
feature.quick_assistant.enabled Feature.quickAssistant camelCase 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

File Purpose
src/shared/data/preference/preferenceSchemas.ts Schema interface and default values
src/shared/data/preference/preferenceTypes.ts Custom 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