# 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 `<input>` 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
```
