3.6 KiB
CLAUDE.md
Context file for Claude Code. Read this before making changes. See docs/ARCHITECTURE.md for deep technical rationale.
Project
Native Apple ecosystem client for a self-hosted Outline instance (wiki/knowledge base). Targets iOS, iPadOS, macOS, single SwiftUI multiplatform codebase. Goal is full editing parity with Outline's web app — not a read-only viewer.
Working name: OutlineClient (rename freely — placeholder only, not a branding decision).
License context (do not re-litigate)
Outline server is BSL 1.1 licensed. This restricts hosting/reselling Outline itself as a service. It does not restrict building an API client. This app only ever talks to Outline's public REST/WebSocket API — it never vendors, forks, or redistributes Outline's server source. No license concerns for this codebase.
Architecture summary
- REST layer — Outline's RPC-style API (
/api/*), Bearer token auth (Authorization: Bearer <token>). Handles collections, documents (non-realtime CRUD), search, users. - Realtime collab layer — Outline runs a Hocuspocus server (Yjs CRDT over WebSocket) at
wss://<host>/collaboration/document.<id>. This is the actual editing transport when a document is open — not the REST API. Full parity requires speaking this protocol, including the Yjs awareness protocol for remote cursors/presence. - CRDT engine — YSwift (Swift bindings over
yrs, the Rust Yjs port, via UniFFI). This is the only realistic native path to binary-compatible Yjs sync. It is pre-1.0 / WIP — expect to patch or contribute upstream, don't assume full feature coverage going in. - Editor UI — native TextKit 2 (
NSTextLayoutManager/AttributedString) rendering mapped from Outline's ProseMirror document schema (headings, lists, checklists, tables, code blocks, embeds, comments). This is the largest single scope item in the project — treat it as its own subsystem, not a UI detail.
Phased build order
- Phase 1 — Read/browse + REST-only editing. Auth, collections tree, document list, search, markdown-rendered read view, basic edit-and-PUT (last-write-wins, no live collab). Ship this before touching Yjs at all.
- Phase 2 — Realtime collaboration. Integrate YSwift + Hocuspocus WebSocket, map Yjs XML fragment ↔ native editor state, add awareness/presence.
- Phase 3 — Polish. Offline cache, conflict UI, embeds/attachments, tables, comments, macOS-specific affordances (menu bar, multiple windows).
Do not start Phase 2 work until Phase 1 is stable and shipped to the device. The CRDT/editor layer is high-risk; de-risk everything else first.
Conventions
- Swift, SwiftUI-first. No UIKit/AppKit escape hatches unless SwiftUI genuinely can't do it (flag these explicitly in PR/commit messages).
- Networking:
URLSession+async/await, no third-party HTTP client. - API tokens stored in Keychain only — never UserDefaults, never logged.
- Prefer protocol-oriented boundaries between the REST layer, the CRDT/sync layer, and the editor UI layer so any one of them (especially YSwift) can be swapped without touching the others.
- Target iOS 17+ / macOS 14+ minimum (needed for mature
AttributedString/TextKit 2 APIs).
Do not
- Do not attempt to reimplement Yjs's CRDT algorithm from scratch — use YSwift/yrs. This has been tried by others and is a multi-year effort (see Yrs project history).
- Do not fall back to polling the REST API for "realtime" — it will visibly diverge from true collaborative editing and defeats the point of this project.
- Do not vendor or copy Outline's server source into this repo.