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).
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: 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
- 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