Skip to content

Project structure

DemocracyDefenderModManager/
├── src/                     SvelteKit frontend
│   ├── routes/               pages: Mods (+page.svelte), Settings, Create, Help
│   └── lib/
│       ├── components/       reusable Svelte components (popups, toggle switch, ...)
│       ├── models/            frontend-side TS models mirroring the Rust ones (Mod, manifest, profile, settings)
│       ├── state/             Svelte state/stores (localization, popup manager)
│       ├── services/          supporting services (e.g. localization loading)
│       ├── types/             shared TS types (popups, command results, UUID)
│       ├── utils/             helpers, including the typed Tauri command wrappers (commands.ts)
│       └── locales/           UI strings (en.json)
├── src-tauri/                Rust/Tauri backend
│   ├── src/
│   │   ├── commands/           #[tauri::command] entry points: mods.rs, profiles.rs, settings.rs, updates.rs, nexus.rs, plus deploy/purge in mod.rs
│   │   ├── models/              manifest.rs, profile.rs, settings.rs, and the shared Mod type
│   │   ├── archive/             zip/7z/rar handling and extraction hardening
│   │   ├── sources.rs            provider-neutral Source resolution and the .hd2mm-origin.json sidecar
│   │   ├── download.rs           direct-URL archive downloading
│   │   ├── providers/            per-site update checks (AyakaMods, GitHub, GameBanana, ModWorkshop, Nexus Mods)
│   │   ├── secrets.rs            the optional Nexus API key: OS keychain / Linux 0600-file fallback, redaction
│   │   ├── utils.rs              small filesystem helpers (recursive copy, case-insensitive path matching)
│   │   └── lib.rs                Tauri app setup: plugins, app state, command registration
│   └── Cargo.toml
├── docs/                     this documentation site (MkDocs + Material)
├── mkdocs.yml
└── .github/workflows/         build.yml (release builds), docs.yml (this site)

How a command call flows

The frontend calls into Rust exclusively through src/lib/utils/commands.ts, which wraps Tauri's invoke() around each #[tauri::command] in src-tauri/src/commands/. State on the Rust side (the loaded mod list) is held in AppState, behind a tokio::Mutex, for the lifetime of the app — see src-tauri/src/lib.rs.

Models mirror both sides

Data types that cross the Tauri boundary — the manifest formats, Profile/Config, Settings — are defined once in Rust (src-tauri/src/models/) with serde PascalCase (de)serialization, and mirrored by hand as TypeScript types under src/lib/models/ and src/lib/types/. If you change a Rust model's shape, the matching TypeScript type needs updating too.