Adding a New Feature End-to-End
The standard workflow
- Define the model struct(s) in
src/core/src/models/<domain>.rs. - Implement repository functions in
src/core/src/repositories/<domain>.rs(all SQL + ownership checks). - Add thin
#[tauri::command]functions insrc/core/src/commands/<domain>.rs. - Register each new command in
tauri::generate_handler![...]insrc/core/src/lib.rs. - Add typed wrapper functions in
src/ui/lib/tauri.ts— the only place the frontend callsinvoke(). - Build UI components/pages that consume the new wrapper via the service layer (
src/ui/services/). - Add Rust integration tests (
src/core/tests/) and, where relevant, Vitest tests (src/ui/test/, colocated__tests__/).
See Backend Architecture, Frontend Architecture, and Writing New Tests for the conventions each step should follow.
Worked example: export_test_to_zip (shipped in 1.0.0-beta.8)
RELEASES.md records the real implementation sequence for this feature, in chronological order (oldest → newest commit):
docs: add export test to zip spec— the product spec (docs/spec/export-test-to-zip.md) landed first.core: add zip crate dependency for test export— added thezipcrate toCargo.toml.core: add export_service to build importable test JSON and zip bundles— the service layer (src/core/src/services/export_service.rs) that assembles JSON + collects media.core: register export_service module— wired the new service module intosrc/core/src/services/mod.rs.core: register export command module— addedcommands::exporttosrc/core/src/commands/mod.rs.core: add export_test_to_zip Tauri command— the thin command wrapper (src/core/src/commands/export.rs).core: wire up export_test_to_zip in invoke_handler— registered inlib.rs'sgenerate_handler!.ui: add exportTestToZip invoke wrapper and ExportResult type— the typed wrapper landed insrc/ui/lib/tauri.ts.ui: add exportContent helper to contentService— a service-layer helper wrapping the raw IPC call with UI-facing concerns (toasts, etc.).ui: add Export action to Content Library 3-dot menu— the actual UI entry point, positioned between Edit and Delete.core: nest export zip contents inside a slug-named folder— refined the zip layout based on early testing.Add tauri-plugin-dialog dependency for export file picker→Regenerate Tauri ACL schemas for tauri-plugin-dialog→Register tauri-plugin-dialog in app builder— added native Save-As dialog support, including regenerating Tauri's ACL/capabilities schema (required whenever a new plugin is added — see File System & Native Dialog Integration).Split export service into build_export + default_export_dir for file picker→Wire native Save As dialog into export_test_to_zip command— connected the dialog to the export flow.Update exportTestToZip to return ExportResult | null on cancel→Propagate nullable export result through exportContent→Handle cancelled export (null result) without error toast— handled the case where the admin cancels the save dialog, without treating cancellation as an error.
This sequence is a good template for planning any new feature that spans both layers: spec → dependency → service → command → registration → frontend wrapper → frontend UI → refinement/edge cases. See Exporting Tests as ZIP Archives for the finished feature's full behavior.