More FE
I prompted the LLM to look at the src/dockb/controllers/README_API.md file and suggest a good breakdown for the remaining server-side work to support the FE. It suggested the following, which seemed good to me:
Phase 1: API Data Contracts (no dependencies)
- Pydantic wire-format schemas
- ProseMirror serialization
Phase 2: Repository Read Methods (depends on models — already done)
- List/find queries on repositories
Phase 3: Service Layer (depends on parts 2 and 3)
- CRUD services
- SessionContext enhancement
Phase 4: Controllers + App Wiring (depends on parts 1–5)
- FastAPI route handlers
- Response middleware
- App assembly
Phase 5: History (independent of Phases 3–4, can run in parallel)
- History infrastructure
- History service + API endpoints
I started working through the 10 components, in an afternoon on the weekend, in between doing other things, and it changed 15 files, and created 22. I then started to think about the actual FE.
FE Design Summary from README Files
Core Editing Principle (README_editing.md)
- FE sends whole sentences when editing — not character diffs
- Sentence splits are detected by the BE (via spaCy), not the FE
- The BE notifies the FE of splits via async notifications
- Paragraph changes = gaining/losing sentences; Chapter changes = gaining/losing paragraphs
- FE uses batching/debounce (from README_API.md): rapid keystrokes are coalesced into a single POST/PUT
Wire Format (README_API.md)
- ProseMirror JSON for Chapter, Paragraph, Sentence nodes
- Tokens are server-only (never transmitted)
- Document is plain JSON (attrs + chapter summaries), not ProseMirror
Session Architecture (infrastructure/session/README.md)
- Per-user SessionContext with JobQueue, DocCache, and notification queue
- OAuth token validation via TokenValidator → account ID
- UserStore persists profiles in TinyDB
- SessionManager owns all active sessions
Async Processing (services/semantics/README.md)
- JobQueue with single background worker thread
- ReconstructJob / DeleteJob / CommitJob pipeline
- Sentence split detection during reconstruction
- Jobs can be cancelled if a new edit arrives
Data States (README_data_states.md)
- NEW, SYNC, CHANGED, DELETED, _ (Nothing)
- Models auto-manage state on parent/child changes
No dedicated FE README exists
The FE design is distributed across the API spec and editing README. The key FE facts are:
- ProseMirror-based editor
- Debounced keystroke batching
- Receives async notifications (sentence/paragraph splits) via piggy-back or polling
- Never sees tokens — only text nodes
- Tells the BE what changed (which node, relative to hierarchy), not where (character offsets)
On closer analysis, I realised the TUI FE would be a waste of time, and that the RUST FE was not going to work so well, and that using a TipTap FE would work better.
DockB Frontend
React + Tiptap editor for the DockB document hierarchy.
Tech Stack
- React 18 — UI framework
- Tiptap 2 — ProseMirror-based rich text editor
- Vite — Build tool with dev server proxy to backend
- TypeScript — Type safety matching backend wire format
Architecture
The frontend communicates with the FastAPI backend via REST endpoints defined in `src/dockb/controllers/README_API.md`. All document content uses ProseMirror JSON wire format (chapter → paragraph → sentence → text).
Document Hierarchy
Document (plain JSON)
└── Chapter (ProseMirror tree)
└── Paragraph (ProseMirror tree)
└── Sentence (ProseMirror tree)
└── Text (inline)
Wire Format Mapping
| Backend Node | Tiptap Extension | Top Node |
|---|---|---|
| Chapter | Chapter | Yes |
| Paragraph | Paragraph | No |
| Sentence | Sentence | No |
| Text | Built-in | No |
Key Design Decisions
- Tokens are server-only— spaCy tokenization happens in the backend; the frontend only sees text nodes
- Semantic events, not text diffs— the frontend sends whole sentence/paragraph replacements; the backend handles sentence splitting asynchronously
- IDs are UUIDs v4— generated by creator (frontend or backend), no temp-ID mapping
- Notifications— sentence/paragraph splits arrive via piggy-back on mutation responses or polling `GET /api/notifications`
Structure
frontend/
├── src/
│ ├── api/client.ts # API client for all REST endpoints
│ ├── editor/
│ │ ├── schema.ts # ProseMirror schema matching backend
│ │ ├── ChapterEditor.tsx # Tiptap editor for chapter content
│ │ └── extensions.ts # Custom Tiptap node extensions
│ ├── types/index.ts # TypeScript types matching wire format
│ ├── components/
│ │ ├── DocumentList.tsx # Document browser
│ │ └── ChapterView.tsx # Chapter sidebar + editor
│ ├── App.tsx # Main app with navigation
│ └── main.tsx # Entry point
├── package.json
├── vite.config.ts # Dev server + API proxy
└── .eslintrc.cjs
Development
cd frontend
npm install
npm run dev # Serves on :3000, proxies /api to :8000
The Vite dev server proxies `/api` requests to `http://localhost:8000` where the FastAPI backend runs.
To run the back end:
make run
Commands
| Command | Purpose |
|---|---|
npm run dev | Start dev server |
npm run build | TypeScript check + production build |
npm run lint | ESLint check |
Now an admission
I got impatient. I'm a newbie with the frontend tech that I'm using, so I told the LLM to build a UI.
It did. But...
it did not show any documents, because there are none yet, but it did not give me the option to create one.
So I worked with it (not so impatiently this time), using good design and TDD principles, and it created a "New document" link.
The UI was extremely bare bones, un-styled, but it worked.
Then when I created the document, the FE showed a blank screen. The reason being that the BE had a bug...
and this bug was because I had not properly checked the tests before I told the LLM to proceed.
My admission is that it is very easy to trust too much, be overly enthusiastic, and skip good practices. I did that.
This does concern me, that I, as an experienced developer, who firmly believes in good programming practices, in my own project (of which I am proud and not under pressure), could succumb to this weakness.
We will need to teach junior engineers:
- Grass roots programming - how about some assembly, and C?
- Best principles
And we absolutely SHOULD NOT put them under pressure, because the temptation to cut corners will be so easy to take, even without pressure, simply from enthusiasm.