package db import "time" // User is an account. With auth deferred, the app runs as a single hardcoded // `local` user (see LocalUserID); the user_id columns and this type exist so // real auth can drop in later without a schema migration. type User struct { ID string `json:"id"` Email string `json:"email"` DisplayName string `json:"display_name"` CreatedAt time.Time `json:"created_at"` } // Document is a single piece of writing. `Content` is the Tiptap JSON document // (source of truth for the editor); `ContentText` is the flattened plain text // kept in sync on every save and fed to the LLM. type Document struct { ID string `json:"id"` UserID string `json:"user_id"` Title string `json:"title"` Content string `json:"content"` // Tiptap JSON ContentText string `json:"content_text"` // plain text for the LLM Tone string `json:"tone"` // target writing tone; steers LLM advice WordCount int `json:"word_count"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // DocumentVersion is a point-in-time snapshot of a document's body, captured so // a writer can recover from a bad edit or an unwanted change. `Content` mirrors // the document's Tiptap JSON at snapshot time; `Kind` records why it was taken // (see the kind constants). List responses omit the heavy Content/ContentText // fields (the `omitempty`-friendly zero strings) and load them only on preview // or restore. type DocumentVersion struct { ID string `json:"id"` DocID string `json:"doc_id"` Title string `json:"title"` Content string `json:"content,omitempty"` // Tiptap JSON; omitted in list view ContentText string `json:"content_text,omitempty"` // plain text; omitted in list view WordCount int `json:"word_count"` Kind string `json:"kind"` // auto | manual | pre_restore CreatedAt time.Time `json:"created_at"` } // Document version kinds, mirrored from the schema CHECK constraint. const ( VersionKindAuto = "auto" // throttled background snapshot on save VersionKindManual = "manual" // explicit "save a restore point" VersionKindPreRestore = "pre_restore" // safety copy taken just before a restore ) // Suggestion is a single LLM-proposed edit anchored to a span of the document. // // FromPos/ToPos are plaintext offsets into ContentText for server-side use only; // the frontend re-anchors by matching the `Original` string in ProseMirror // coordinates at render time (spec Note #6). `Replacement` is empty for `voice` // flags — those are awareness-only, with no correction to apply. type Suggestion struct { ID string `json:"id"` DocID string `json:"doc_id"` FromPos int `json:"from_pos"` ToPos int `json:"to_pos"` Original string `json:"original"` Replacement string `json:"replacement"` Explanation string `json:"explanation"` Type string `json:"type"` // grammar | phrasing | idiom | clarity | voice Status string `json:"status"` // pending | accepted | rejected CreatedAt time.Time `json:"created_at"` } // Suggestion type and status values, mirrored from the schema CHECK constraints. const ( SuggestionTypeGrammar = "grammar" SuggestionTypePhrasing = "phrasing" SuggestionTypeIdiom = "idiom" SuggestionTypeClarity = "clarity" SuggestionTypeVoice = "voice" SuggestionStatusPending = "pending" SuggestionStatusAccepted = "accepted" SuggestionStatusRejected = "rejected" )