Tauri Config & IPC (MarkerOn)
Architecture
config.json (disk, camelCase JSON)
↔ config.rs (Rust structs, serde rename)
↔ commands.rs (#[tauri::command])
↔ lib.rs (generate_handler! registration)
↔ src/types/app.d.ts (TypeScript types)
↔ Vue (invoke get_config / save_*)
Config path: app config dir → config.json (config::config_path), or {exe_dir}/data/config.json when a markeron.portable marker file is next to the executable.
Naming convention
| Layer | Convention | Example |
|---|---|---|
| JSON / TS / Vue | camelCase | enableDragging, toggleDrawing |
| Rust field | snake_case + #[serde(rename = "...")] |
enable_dragging ↔ enableDragging |
Always add #[serde(default)] (or default = "fn") on new fields so old config.json files deserialize.
Checklist: add a general setting
Example: new boolean fooBar in settings.
1. Rust — src-tauri/src/config.rs
// In GeneralConfig:
#[serde(default, rename = "fooBar")]
pub foo_bar: bool,
// In Default for GeneralConfig:
foo_bar: false,
// In normalized() if value needs clamping/validation
2. TypeScript — src/types/app.d.ts
general: {
// ...
fooBar: boolean
}
3. Command — usually no change
General settings use existing save_general (commands.rs). It saves GeneralConfig, emits config-changed, persists to disk.
New top-level config section → new struct + new save_* command.
4. Frontend UI
- Settings window:
GeneralTab.vueorSettingsView.vue - Pattern: optimistic
emit('update:…')→get_config→ mutatecfg.general→invoke('save_general', { general: cfg.general }) - Overlay runtime:
DrawingOverlay.vueloads config on mount and listens:
await listen<AppConfig>('config-changed', (event) => { /* apply */ })
If the overlay must react live, ensure save_general emits config-changed (already does).
5. Tests
- Rust: add/update tests in
config.rs#[cfg(test)]module (defaults, normalization, serde round-trip). - Frontend: only if new behavior in composables.
Checklist: add a new IPC command
- Implement in
src-tauri/src/commands.rs(orclipboard.rsetc.) with#[tauri::command] - Register in
lib.rs→generate_handler![..., commands::your_fn] - Call from Vue:
invoke('your_fn', { arg: value })— args use camelCase in JS, match command param names - Return
AppResult<T>for errors surfaced to UI
Existing commands:
| Command | Purpose |
|---|---|
get_config |
Read full AppConfig |
save_shortcuts |
Global hotkeys; rolls back on invalid/register failure |
save_general |
General settings + config-changed event |
save_locale |
Persist locale + Rust i18n + tray + window title |
exit_drawing |
Leave annotation mode |
open_url |
Allowed URLs only (GitHub, Microsoft Store) |
copy_screen / copy_whiteboard |
Clipboard |
Checklist: add / change shortcuts
Shortcuts are not in GeneralConfig — they live in Shortcuts struct.
Touch:
config.rs—Shortcuts,default_shortcuts()(macOS usesCommand+, othersCtrl+)commands.rs—save_shortcutsvalidationshortcuts.rs— register handlers + unit testsSettingsView.vue— capture UIsrc/types/app.d.ts—shortcutskeysREADME.md+README_zh.md— shortcut tables
Agent rules
- Never break backward compatibility: new fields need serde defaults.
- Keep TS
AppConfigin sync with RustAppConfig/ nested structs. - Do not add invoke commands without registering in
generate_handler!. - Prefer extending
save_generalover one-command-per-toggle unless logic is distinct (likesave_locale).
Related
- Locale strings:
.cursor/rules/i18n.mdc - Platform window behavior:
src-tauri/src/lib.rs,macos.rs,win32.rs