# 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:**

```typescript
// 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.

```typescript
// 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.

```typescript
// 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`

```typescript
// 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:

```typescript
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`:

```typescript
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

```typescript
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

## Related Documentation

- [Preference Overview](./preference-overview.md) - Architecture and sync mechanism
- [Preference Usage](./preference-usage.md) - Hooks and service API
