//! Serde shapes for the local store. Output structs mirror the JSON the frontend //! already consumes (frontend/src/stores/*.ts) so the same components render whether //! the data comes from REST or from these Tauri commands. Field names/optionality //! match the TypeScript interfaces exactly. use serde::{Deserialize, Serialize}; #[derive(Serialize)] pub struct Note { pub id: String, pub title: Option, /// title if set, else the note's first body line — always present, so body-only /// notes are still nameable and `[[link]]`-able. Derived, never stored. pub display_title: String, pub body: String, pub color: String, pub kind: String, pub position: i64, pub pinned: bool, pub archived: bool, pub trashed: bool, /// When it was trashed (null unless trashed). Named for the server's field so the /// shared frontend counts down the retention window identically either way. pub deleted_at: Option, pub remind_at: Option, pub recurrence: Option, pub labels: Vec, pub items: Vec, pub attachments: Vec, pub previews: Vec, pub created_at: Option, pub updated_at: Option, } #[derive(Serialize)] pub struct NoteLabel { pub id: String, pub name: String, pub color: String, /// True when attached because of a `#tag` in the body (kept in sync with the text). pub via_tag: bool, } #[derive(Serialize)] pub struct ChecklistItem { pub id: String, pub text: String, pub checked: bool, pub position: i64, } #[derive(Serialize)] pub struct Attachment { pub id: String, pub url: String, pub filename: Option, pub mime: String, pub size: Option, pub sha256: Option, } #[derive(Serialize)] pub struct LinkPreview { pub id: String, pub url: String, pub title: Option, pub description: Option, pub image_url: Option, pub site_name: Option, } #[derive(Serialize)] pub struct NoteRevision { pub id: String, pub title: Option, pub body: String, pub created_at: Option, } #[derive(Serialize)] pub struct Label { pub id: String, pub name: String, pub color: String, // Note count is only meaningful in listings; omitted elsewhere so the frontend // preserves the count it already holds (matches the REST single-label responses). #[serde(skip_serializing_if = "Option::is_none")] pub count: Option, } #[derive(Serialize)] pub struct TitleEntry { pub id: String, pub title: String, } #[derive(Serialize)] pub struct Backlink { pub id: String, pub title: String, } #[derive(Serialize)] pub struct SavedFilter { pub id: String, pub name: String, /// Mirrors NoteFacets — stored as a JSON blob, round-tripped opaquely. pub params: serde_json::Value, pub position: i64, } #[derive(Serialize)] pub struct PublicConfig { pub site_name: String, pub allow_registration: bool, pub version: String, pub enable_url_unfurl: bool, pub trash_retention_days: u32, } /// The synthetic single user the offline core reports, so the app's auth-gated /// router resolves without a login. There is no real account offline. #[derive(Serialize)] pub struct User { pub id: String, pub email: String, pub display_name: String, pub email_verified: bool, pub is_admin: bool, } fn default_color() -> String { "default".to_string() } #[derive(Deserialize)] pub struct NoteCreateInput { #[serde(default)] pub title: String, #[serde(default)] pub body: String, #[serde(default = "default_color")] pub color: String, #[serde(default)] pub kind: Option, #[serde(default)] pub items: Option>, } /// The board query (mirrors adapters/repo.ts NoteListQuery). `labelId` is camelCase /// on the wire; the facet fields are snake_case, matching NoteFacets. #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct ListQuery { pub view: String, #[serde(default)] pub label_id: Option, #[serde(default)] pub facets: Option, #[serde(default)] pub sort: Option, } #[derive(Deserialize)] pub struct Facets { #[serde(default)] pub q: Option, #[serde(default)] pub color: Option, #[serde(default)] pub kind: Option, #[serde(default)] pub label: Option>, #[serde(default)] pub has_reminder: Option, #[serde(default)] pub has_attachment: Option, #[serde(default)] pub created_after: Option, #[serde(default)] pub created_before: Option, }