# Enesis A warm, literary journal app. Built with Tauri, Nuxt UI, and the `@enesis/editor` block markdown engine. Daily notes are flat markdown files (`journals/YYYY-MM-DD.md`) — readable anywhere, no lock-in. A SQLite index provides full-text search and backlinks. The visual identity (Hearthside theme) leans into warmth: Lora serif, clay primary, stone neutrals, earthy semantics. ## Quick start ```bash pnpm install pnpm generate # static build pnpm tauri dev # or: pnpm tauri build ``` ## Stack | Layer | Technology | |---|---| | Desktop shell | Tauri v2 (Rust) | | Frontend | Nuxt 3 + Vue 3 + TypeScript | | UI framework | Nuxt UI v4 | | Editor engine | `@enesis/editor` (Lezer-based block markdown) | | Index | SQLite via `tauri-plugin-sql`, FTS5 | | File watching | `notify` crate | ## Theme — Hearthside Warm, earthy, literary. Lora serif throughout, clay primary, orange secondary, stone neutral base, rose/emerald/amber/sky for semantics. Configured in `app.config.ts` and `assets/main.css`. ## Development ```bash pnpm dev # Vite dev server pnpm tauri dev # Tauri desktop dev pnpm generate # Static pre-render (for Tauri) pnpm test:e2e # Playwright E2E (in packages/editor) ``` ## Project structure ``` src/ assets/main.css # Tailwind + theme tokens + font app.config.ts # Nuxt UI color mappings components/ JournalDay.vue # One Editor per day JournalView.vue # Reverse-chronological feed TimelineRail.vue # Density dot timeline AppRightSidebar.vue # References + search layouts/default.vue # Sidebars + editor shell composables/ # useFileSystem, useDates, etc. stores/ # Pinia: workspace, ui lib/ # Indexer, schema, parse helpers src-tauri/ src/lib.rs # Rust commands + plugins icons/ # App icons (clay block logo) public/ favicon.svg # Clay block stacking logo ``` ## User guide — Editor behaviors ### Auto-save Content is saved to disk **500ms after you stop typing**. Pending saves are flushed immediately when navigating away, so you won't lose edits from quick page switches. ### Page creation **No file is created until you type.** Clicking a `[[link]]` navigates to the page's path and shows an empty editor. The file is written to disk only when you add content. If you navigate away without typing, nothing is saved. This keeps your workspace clean of empty placeholder files. The conventional location for new pages is the `pages/` directory (e.g. `pages/My Note.md`). You can change this in Settings. ### Renaming a page The title at the top of a page is an editable `` styled as a heading. Changing it renames the underlying file on disk. Press `Enter` to confirm, `Escape` to cancel. Note: existing `[[links]]` in other pages are not updated — that's a planned enhancement. ### Tags Clicking a `#tag` opens a virtual aggregation page showing all blocks that reference it, regardless of which file they're in. This is a read-only view — no file on disk until you type content here (future feature). ### Backlinks At the bottom of every page, a collapsible "Linked references" section shows which other pages link to this one. Each result includes the source page name and a contextual excerpt with the `[[link]]` highlighted. Clicking a backlink navigates to the source page. ### Search (coming in D6) Full-text search across the SQLite index is planned but not yet wired to the sidebar. The FTS5 index is built, the query logic is ready, but the UI is not connected. --- ## Index two-flow architecture The SQLite index is rebuilt on two paths with different trade-offs: ### Flow 1 — App startup (`syncIndex`) Runs every time the app launches. Avoids unnecessary work: 1. **List all `.md` files** from the filesystem 2. **Read and hash each file** (djb2, stored in `pages.content_hash`) 3. **Compare hash** against the stored value for that file 4. **Unchanged** → skip entirely (no `splitMarkdownIntoBlocks`, no SQL writes) 5. **New/changed** → call `indexFile` (block-level diff, only writes changed blocks) 6. **Deleted** → cascade remove from index (blocks, links, FTS) Files whose content hasn't changed cost one hash computation and a `SELECT` query. No block parsing, no link extraction, no write amplification. ### Flow 2 — Manual reindex (`fullReindex`) Triggered via **File → Reindex Library** (`Cmd+Shift+R`). Wipes the entire index (`DELETE FROM pages` — FK cascade handles blocks, links, FTS) and re-indexes every `.md` file from scratch. Use when you suspect corruption or want a guaranteed clean state. ### File watcher (incremental) Between these two flows, the `notify`-based file watcher handles individual file changes: - **Modified/created** → `indexFile` on the single file - **Deleted** → `removeFile` (cascade) The watcher starts before sync completes. If a file changes mid-sync, the watcher re-indexes it independently — worst case a stale entry that corrects on the next change. --- ## Developer notes — D4 Page Model For implementation details, data flow diagrams, and edge case documentation, see `AGENTS.md` in the repository root. Key sections: - **Route architecture**: catch-all `[...slug].vue` classifies paths (page vs tag) - **Data flow**: event chain from `EditorBlock` click → `usePageNavigation` → router push - **Indexer title fallback**: `computePageTitle()` ensures every page has a non-empty title - **Edge cases**: JSDoc `@remarks` on each component and function list deferred work ### Tests ```bash pnpm test # vitest run in apps/tauri biome check src/ # run from apps/tauri ```