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
-
Semantic Grouping: Group related settings under common namespaces
app.*- Application-level settingschat.*- Chat/message settingsfeature.*- Feature togglesui.*- UI/theme settingsdata.*- Data/backup settingsshortcut.*- 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
-
Hierarchy: Use dots for hierarchy, underscores for multi-word names
chat.message.font_size(notchat.messageFontSize)feature.quick_assistant.enabled(notfeature.quickAssistant.enabled)
-
Boolean Naming: Use positive names with
.enabledsuffix for togglesfeature.quick_assistant.enabled(notfeature.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:
- Visibility: Individual config items are more explicit and discoverable
- 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: packages/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: packages/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 |
|---|---|
packages/shared/data/preference/preferenceSchemas.ts |
Schema interface and default values |
packages/shared/data/preference/preferenceTypes.ts |
Custom type definitions (enums, unions) |
Best Practices Summary
- Flat over nested: Split objects into individual keys unless frequently accessed as a whole
- Atomic values: One preference = one logical setting
- Sensible defaults: All preferences must have default values
- Consistent naming: Follow
namespace.category.key_namepattern - 2-3 levels: Don't over-nest; 2-3 dot-separated segments is usually sufficient
Related Documentation
- Preference Overview - Architecture and sync mechanism
- Preference Usage - Hooks and service API