aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/dev/roadmap.md388
-rw-r--r--docs/manual/features.md17
2 files changed, 46 insertions, 359 deletions
diff --git a/docs/dev/roadmap.md b/docs/dev/roadmap.md
index 1cd3e99..ea9a70d 100644
--- a/docs/dev/roadmap.md
+++ b/docs/dev/roadmap.md
@@ -1,370 +1,48 @@
-# Note Feature Implementation Roadmap
+# Kioku Development Roadmap
-This document outlines the implementation plan for adding Anki-compatible "Note" concept to Kioku.
+## Issue #3: Introduce CRDT Library for Conflict Resolution
-## Overview
+Replace the current Last-Write-Wins (LWW) conflict resolution with Automerge CRDT for better offline sync.
-Currently, Kioku uses a simple Card model with `front` and `back` text fields. To improve Anki interoperability, we will introduce:
+**Decisions:**
+- Library: Automerge
+- Text conflicts: LWW Register (simple, predictable)
+- Migration: Clean migration (no backward compatibility)
-1. **Note** - A container for field values (similar to Anki's Note)
-2. **NoteType** - Defines the structure of notes (fields and card templates)
-3. **Card** - Generated from Notes, holds FSRS scheduling state
+### Phase 1: Add Automerge and Core Types
-### Key Concepts
+- [ ] Install dependencies: `@automerge/automerge`, `@automerge/automerge-repo`, `@automerge/automerge-repo-storage-indexeddb`
+- [ ] Create `src/client/sync/crdt/types.ts` - Automerge document type definitions
+- [ ] Create `src/client/sync/crdt/document-manager.ts` - Automerge document lifecycle management
+- [ ] Create `src/client/sync/crdt/index.ts` - Module exports
-- **One Note → Multiple Cards**: A "Basic (and reversed)" note type creates 2 cards (front→back, back→front)
-- **One Note → One Card**: A "Basic" note type creates 1 card
-- **Shared Content**: When note content is edited, all generated cards reflect the change
-- **Independent Scheduling**: Each card has its own FSRS state (due date, stability, etc.)
+### Phase 2: Create CRDT Repository Layer
-## Data Model Design
+- [ ] Create `src/client/sync/crdt/repositories.ts` - CRDT-aware repository wrappers
+- [ ] Create `src/client/sync/crdt/sync-state.ts` - Sync state serialization
-### New Entities
+### Phase 3: Modify Sync Protocol
-```
-NoteType
-├── id: UUID
-├── user_id: UUID (FK → users)
-├── name: string
-├── front_template: string (mustache template, e.g., "{{Front}}")
-├── back_template: string (mustache template, e.g., "{{Back}}")
-├── is_reversible: boolean (if true, creates reversed card too)
-├── created_at: timestamp
-├── updated_at: timestamp
-├── deleted_at: timestamp (soft delete)
-└── sync_version: number
+- [ ] Modify `src/client/sync/push.ts` - Add crdtChanges to push payload
+- [ ] Modify `src/client/sync/pull.ts` - Handle crdtChanges in pull response
+- [ ] Modify `src/client/sync/conflict.ts` - Replace LWW with Automerge merge
+- [ ] Modify `src/client/sync/manager.ts` - Integrate CRDT sync flow
-NoteFieldType
-├── id: UUID
-├── note_type_id: UUID (FK → note_types)
-├── name: string (e.g., "Front", "Back")
-├── order: number (display order)
-├── field_type: enum ("text") // Fixed to "text" for now
-├── created_at: timestamp
-├── updated_at: timestamp
-├── deleted_at: timestamp
-└── sync_version: number
+### Phase 4: Server-Side CRDT Support
-Note
-├── id: UUID
-├── deck_id: UUID (FK → decks)
-├── note_type_id: UUID (FK → note_types)
-├── created_at: timestamp
-├── updated_at: timestamp
-├── deleted_at: timestamp
-└── sync_version: number
+- [ ] Install server dependency: `@automerge/automerge`
+- [ ] Create `src/server/db/schema-crdt.ts` - CRDT document storage schema
+- [ ] Create database migration for crdt_documents table
+- [ ] Modify `src/server/routes/sync.ts` - Handle CRDT changes in API
+- [ ] Modify `src/server/repositories/sync.ts` - Store/merge CRDT documents
-NoteFieldValue
-├── id: UUID
-├── note_id: UUID (FK → notes)
-├── note_field_type_id: UUID (FK → note_field_types)
-├── value: text
-├── created_at: timestamp
-├── updated_at: timestamp
-└── sync_version: number
+### Phase 5: Migration
-Card (modified)
-├── id: UUID
-├── deck_id: UUID (FK → decks)
-├── note_id: UUID (FK → notes) [REQUIRED]
-├── is_reversed: boolean [REQUIRED] (false=normal, true=reversed)
-├── front: text [CACHED - rendered from template for performance]
-├── back: text [CACHED - rendered from template for performance]
-├── (FSRS fields unchanged)
-├── created_at: timestamp
-├── updated_at: timestamp
-├── deleted_at: timestamp
-└── sync_version: number
-```
+- [ ] Create `src/client/sync/crdt/migration.ts` - One-time migration script
+- [ ] Create server migration script to convert existing data
-### Card Display Logic
+### Phase 6: Testing and Cleanup
-Template rendering uses a custom mustache-like renderer (no external dependencies).
-
-Syntax: `{{FieldName}}` is replaced with the field value.
-
-When displaying a card:
-- **Normal card** (`is_reversed = false`): Render `front_template` on front, `back_template` on back
-- **Reversed card** (`is_reversed = true`): Render `back_template` on front, `front_template` on back
-
-Example templates:
-- Simple: `{{Front}}`
-- With text: `Q: {{Front}}`
-- Multiple fields: `{{Word}} - {{Reading}}`
-
-### Built-in Note Types
-
-Create these as default note types for each user:
-
-1. **Basic**
- - Fields: Front, Back
- - front_template: `{{Front}}`, back_template: `{{Back}}`
- - is_reversible: false
-
-2. **Basic (and reversed card)**
- - Fields: Front, Back
- - front_template: `{{Front}}`, back_template: `{{Back}}`
- - is_reversible: true
-
-### Behavior Rules
-
-**Updating `is_reversible`:**
-- Changing `is_reversible` does NOT affect existing cards
-- Only affects new note creation (whether to create 1 or 2 cards)
-- Existing reversed cards remain even if `is_reversible` is set to `false`
-
-**Deletion Constraints (enforced by FK constraints + application logic):**
-- **NoteType**: Cannot delete if any Notes reference it
-- **NoteFieldType**: Cannot delete if any NoteFieldValues reference it
-- **Note**: Deleting a Note cascades soft-delete to all its Cards
-- **Card**: Deleting a Card also deletes its Note (and all sibling Cards via cascade)
-
-**Required Tests for Deletion:**
-- [x] Attempt to delete NoteType with existing Notes → should fail
-- [x] Attempt to delete NoteFieldType with existing NoteFieldValues → should fail
-- [x] Delete Note → verify all related Cards are soft-deleted
-- [x] Delete Card → verify Note and all sibling Cards are soft-deleted
-
-## Implementation Phases
-
-### Phase 1: Database Schema
-
-**Tasks:**
-- [x] Add `note_types` table schema (Drizzle)
-- [x] Add `note_field_types` table schema
-- [x] Add `notes` table schema
-- [x] Add `note_field_values` table schema
-- [x] Modify `cards` table: add `note_id`, `is_reversed` columns (nullable initially)
-- [x] Create migration file
-- [x] Add Zod validation schemas
-
-**Files to modify:**
-- `src/server/db/schema.ts`
-- `src/server/schemas/index.ts`
-- `drizzle/` (new migration)
-
-### Phase 2: Server Repositories
-
-**Tasks:**
-- [x] Create `NoteTypeRepository`
- - CRUD operations
- - Include fields when fetching
-- [x] Create `NoteTypeFieldRepository`
- - CRUD operations
- - Reorder fields
-- [x] Create `NoteRepository`
- - Create note with field values (auto-generate cards based on `is_reversible`)
- - Update note (updates field values)
- - Delete note (cascade soft-delete to cards)
-- [x] Modify `CardRepository`
- - Fetch card with note data for display
- - Support note-based card creation
-
-**Files to create/modify:**
-- `src/server/repositories/noteType.ts` (new)
-- `src/server/repositories/note.ts` (new)
-- `src/server/repositories/card.ts` (modify)
-- `src/server/repositories/types.ts` (modify)
-
-### Phase 3: Server API Routes
-
-**Tasks:**
-- [x] Add NoteType routes
- - `GET /api/note-types` - List user's note types
- - `POST /api/note-types` - Create note type
- - `GET /api/note-types/:id` - Get note type with fields
- - `PUT /api/note-types/:id` - Update note type (name, front_template, back_template, is_reversible)
- - `DELETE /api/note-types/:id` - Soft delete
-- [x] Add NoteFieldType routes (nested under note-types)
- - `POST /api/note-types/:id/fields` - Add field
- - `PUT /api/note-types/:id/fields/:fieldId` - Update field
- - `DELETE /api/note-types/:id/fields/:fieldId` - Remove field
- - `PUT /api/note-types/:id/fields/reorder` - Reorder fields
-- [x] Add Note routes
- - `GET /api/decks/:deckId/notes` - List notes in deck
- - `POST /api/decks/:deckId/notes` - Create note (auto-generates cards)
- - `GET /api/decks/:deckId/notes/:noteId` - Get note with field values
- - `PUT /api/decks/:deckId/notes/:noteId` - Update note field values
- - `DELETE /api/decks/:deckId/notes/:noteId` - Delete note and its cards
-- [x] Modify Card routes
- - Update GET to include note data when available
-- [x] Modify Study routes
- - Fetch note/field data for card display
-
-**Files to create/modify:**
-- `src/server/routes/noteTypes.ts` (new)
-- `src/server/routes/notes.ts` (new)
-- `src/server/routes/cards.ts` (modify)
-- `src/server/routes/study.ts` (modify)
-- `src/server/index.ts` (register new routes)
-
-### Phase 4: Client Database (Dexie)
-
-**Tasks:**
-- [x] Add `LocalNoteType` interface and table
-- [x] Add `LocalNoteFieldType` interface and table
-- [x] Add `LocalNote` interface and table
-- [x] Add `LocalNoteFieldValue` interface and table
-- [x] Modify `LocalCard` interface: add `noteId`, `isReversed`
-- [x] Update Dexie schema version and upgrade handler
-- [x] Create client repositories for new entities
-
-**Files to modify:**
-- `src/client/db/index.ts`
-- `src/client/db/repositories.ts`
-
-### Phase 5: Sync Logic
-
-**Tasks:**
-- [x] Add sync for NoteType, NoteFieldType
-- [x] Add sync for Note, NoteFieldValue
-- [x] Update Card sync to include `noteId`, `isReversed`
-- [x] Define sync order (NoteTypes → Notes → Cards)
-- [x] Update pull/push sync handlers
-
-**Files to modify:**
-- `src/server/routes/sync.ts`
-- `src/server/repositories/sync.ts`
-- `src/client/sync/push.ts`
-- `src/client/sync/pull.ts`
-
-### Phase 6: Frontend - Note Type Management
-
-**Tasks:**
-- [x] Create NoteType list page (`/note-types`)
-- [x] Create NoteType editor component
- - Edit name
- - Manage fields (add/remove/reorder)
- - Edit front/back templates (mustache syntax)
- - Toggle `is_reversible` option
-- [x] Add navigation to note type management
-
-**Files to create:**
-- `src/client/pages/NoteTypesPage.tsx`
-- `src/client/components/NoteTypeEditor.tsx`
-- `src/client/components/FieldEditor.tsx`
-
-### Phase 7: Frontend - Note CRUD
-
-**Tasks:**
-- [x] Update CreateCardModal → CreateNoteModal
- - Select note type
- - Dynamic field inputs based on note type
- - Preview generated cards
-- [x] Update EditCardModal → EditNoteModal
- - Load note and field values
- - Update all generated cards on save
-- [x] Update DeckDetailPage
- - Group cards by note
- - Show note-level actions (edit note, delete note)
- - Display whether card is normal or reversed
-
-**Files to modify:**
-- `src/client/components/CreateCardModal.tsx` → `CreateNoteModal.tsx`
-- `src/client/components/EditCardModal.tsx` → `EditNoteModal.tsx`
-- `src/client/pages/DeckDetailPage.tsx`
-
-### Phase 8: Frontend - Study Page
-
-**Tasks:**
-- [x] Create custom template renderer utility
-- [x] Update StudyPage to render cards based on note data
- - Fetch note field values
- - Use `isReversed` to determine which template to use for front/back
- - Render templates with custom renderer
- - Maintain front/back flip behavior
-- [x] Handle both legacy cards (direct front/back) and new note-based cards
-
-**Files to modify/create:**
-- `src/client/utils/templateRenderer.ts` (new)
-- `src/client/pages/StudyPage.tsx`
-
-### Phase 9: Cleanup & Documentation
-
-**Tasks:**
-- [x] Make `note_id` and `is_reversed` NOT NULL (schema migration)
-- [x] Update architecture.md with Note-based data model
-- [ ] E2E tests for study flow with note-based cards
-- [ ] Performance testing with multiple cards per note
-
-**Note:** Data migration is not needed since production has no legacy data.
-
-## Design Notes
-
-### Card front/back Fields
-
-Cards retain `front` and `back` text fields as **cached rendered content**. When a card is created from a note:
-1. Templates are rendered with field values
-2. Rendered content is stored in `front`/`back` for quick display
-3. When note content is updated, all related cards are re-rendered
-
-This approach avoids rendering templates on every card display while maintaining the Note as the source of truth.
-
-## API Examples
-
-### Create Note
-
-```http
-POST /api/decks/:deckId/notes
-Content-Type: application/json
-
-{
- "noteTypeId": "uuid-of-basic-reversed",
- "fields": {
- "field-uuid-front": "What is the capital of Japan?",
- "field-uuid-back": "Tokyo"
- }
-}
-```
-
-Response:
-```json
-{
- "note": {
- "id": "note-uuid",
- "noteTypeId": "...",
- "deckId": "...",
- "fieldValues": [...]
- },
- "cards": [
- { "id": "card-1-uuid", "isReversed": false },
- { "id": "card-2-uuid", "isReversed": true }
- ]
-}
-```
-
-### Get Cards for Study
-
-```http
-GET /api/decks/:deckId/study
-```
-
-Response:
-```json
-{
- "cards": [
- {
- "id": "card-uuid",
- "noteId": "note-uuid",
- "isReversed": false,
- "fieldValues": {
- "Front": "What is the capital of Japan?",
- "Back": "Tokyo"
- },
- "state": 0,
- "due": "2024-01-15T00:00:00Z",
- ...
- }
- ]
-}
-```
-
-Note: Templates (`front_template`, `back_template`) are fetched separately via NoteType API and cached on the client. The client renders the card display using the template and field values.
-
-## Testing Checklist
-
-- [x] Unit tests for all new repositories
-- [x] Integration tests for note CRUD
-- [x] Test card generation from different note types
-- [x] Test sync with note data
-- [ ] E2E tests for study flow with note-based cards
+- [ ] Add unit tests for CRDT operations
+- [ ] Add integration tests for concurrent edit scenarios
+- [ ] Remove legacy LWW code after validation
diff --git a/docs/manual/features.md b/docs/manual/features.md
index 81c81ca..cc5db35 100644
--- a/docs/manual/features.md
+++ b/docs/manual/features.md
@@ -8,11 +8,20 @@ A list of features available in Kioku.
- View all decks at a glance
- Confirmation dialog before deletion
-## Card Management
+## Note Types
-- Create, edit, and delete cards within decks
-- Browse all cards in a deck
-- Front/back text fields for flashcards
+- Define custom note structures with configurable fields
+- Built-in note types: "Basic" and "Basic (and reversed card)"
+- Mustache-style templates for card rendering (e.g., `{{Front}}`, `{{Back}}`)
+- Reversible option: automatically generate both normal and reversed cards
+
+## Note & Card Management
+
+- Create notes with dynamic fields based on note type
+- One note can generate multiple cards (e.g., front→back and back→front)
+- Edit note content and all generated cards update automatically
+- Browse cards grouped by note in deck view
+- Independent scheduling: each card maintains its own FSRS state
## Study Session