ayushkokande's picture
Upload folder using huggingface_hub
4ce3e23 verified
|
Raw
History Blame Contribute Delete
7.83 kB
metadata
title: Distributed Collaborative Editor
emoji: πŸ“
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false

Local-First Distributed Collaborative Editor

A distributed, local-first collaborative document system in TypeScript: many independent replicas (browsers, tabs, devices) edit the same document concurrently, propagate updates through a sync relay, persist locally, and converge under partitions and flaky networksβ€”built for real multi-party collaboration, not a toy two-window demo.

Next.js TypeScript Yjs


Why this project

This is a distributed collaborative editor: many nodes, one shared document, independent failure and reconnect. The replication story is first-class:

  • Replicas β€” every browser/tab/device is a full copy of document state (multi-primary editing).
  • Relay β€” WebSocket hub fan-out to the replica fleet; merged state for late joiners and recovery.
  • Offline-first β€” IndexedDB persists snapshots and queues unsynced deltas until reconnect.
  • Eventual consistency β€” concurrent edits converge via CRDT-backed merge (Yjs), not last-write-wins on raw strings.

Good fit for demos, coursework, or portfolio work in distributed systems, local-first, or full-stack TypeScript.


Features

Area What you get
Distributed live editing N replicas (any mix of browsers, devices, tabs) on one doc; relay fan-out keeps the fleet converging in real time.
Offline / partition Keep editing when disconnected; pending updates flush on reconnect.
Multi-tab sync Same machine, multiple tabs stay aligned via BroadcastChannel.
Presence See who is in the document and who is actively editing.
Local durability Document snapshots and outbox stored in IndexedDB.
Server persistence Relay merges Yjs state; summaries persisted under data/ (gitignored).
Dev / demo tools Test panel: simulate offline, force sync, inspect state vectors, clear local data.

Architecture

flowchart TB
  subgraph replicas["Document replicas (unbounded)"]
    R1[Client / tab 1 Β· Y.Doc]
    R2[Client / tab 2 Β· Y.Doc]
    R3[Client N Β· Y.Doc]
  end

  subgraph perReplica["Each replica"]
    IDB[(IndexedDB + outbox)]
    BC[BroadcastChannel]
  end

  subgraph relay["Central sync relay"]
    WS[WebSocket :1234]
    MEM[(Merged state + persistence)]
  end

  R1 --- perReplica
  R2 --- perReplica
  R1 <-->|intra-browser| R2
  R1 & R2 & R3 <-->|Yjs update stream| WS
  WS --> MEM

High-level flow

  1. User edits title/content β†’ Yjs applies local updates.
  2. Online: updates go to the WebSocket relay and to other tabs on the same origin.
  3. Offline: updates are queued in IndexedDB; UI shows pending count.
  4. Reconnect: client sends state / requests catch-up; relay broadcasts merged state; outbox clears after successful persistence.

Tech stack

  • Frontend: Next.js (App Router), React 19, Tailwind CSS, Radix UI
  • Replication: Yjs (CRDT document model)
  • Transport: Node ws WebSocket server (server/websocket-server.ts)
  • Browser storage: IndexedDB (lib/indexeddb.ts)
  • API: Next.js route handlers for document listing (app/api/documents)

Prerequisites

  • Node.js 18+ (20+ recommended)
  • npm, pnpm, or yarn

Quick start

Run two services locally (web app + sync relay). Collaborators can attach as many replicas as you want once both are up.

1. Install dependencies

npm install
# or: pnpm install

2. Start the WebSocket relay

npx tsx server/websocket-server.ts

Default: ws://0.0.0.0:1234 (reachable from other devices on your LAN).

3. Start the web app

In another terminal:

npm run dev

Open http://localhost:3000.

4. Open a document to the fleet

  1. Click New Document (or pick one from the shared list).
  2. Share the document URL with collaboratorsβ€”other laptops on your LAN, phones on Wi‑Fi, extra browser profiles, or multiple tabs on one machine.
  3. Everyone edits at once; the relay broadcasts Yjs updates so all replicas stay aligned. Presence shows who is in the room.

Scale-out pattern: one document ID β†’ many replicas β†’ single relay room β†’ eventual consistency via CRDT merge. Add as many clients as you want; the architecture is N-replica, not pairwise.


Environment variables

Variable Default Description
WS_PORT 1234 WebSocket relay port
NEXT_PUBLIC_WS_URL ws://<hostname>:1234 Client WebSocket URL (set if relay is not on localhost)

Example for a remote relay:

NEXT_PUBLIC_WS_URL=ws://192.168.1.10:1234 npm run dev

Demo ideas (presentations / interviews)

  1. Multi-replica convergence β€” spin up several clients on one doc (tabs + devices); show live edits, presence, and connected-peer counts on the relay.
  2. Network partition β€” take one replica offline (airplane mode or test panel), keep editing, reconnect the fleet, and show outbox drain + state catch-up.
  3. Replication paths β€” contrast inter-tab (BroadcastChannel) vs cross-network (WebSocket relay) as two channels in one distributed editor.
  4. Late joiner β€” open a fresh replica mid-session; relay hydrates merged document state so the new node catches up without a full reset.

Project structure

β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ page.tsx              # Document list / home
β”‚   β”œβ”€β”€ doc/[id]/page.tsx     # Editor route
β”‚   └── api/documents/        # Shared document metadata API
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ document-editor-sync.tsx   # Main editor + sync orchestration
β”‚   β”œβ”€β”€ presence-avatars.tsx
β”‚   └── test-panel.tsx             # Offline simulation & debug
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ use-websocket-sync.ts      # Cross-device sync
β”‚   β”œβ”€β”€ use-tab-sync.ts            # Same-browser tab sync
β”‚   └── use-presence.ts
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ indexeddb.ts               # Local persistence + outbox
β”‚   β”œβ”€β”€ shared-documents.ts        # Server-side JSON persistence
β”‚   └── offline-queue.ts
β”œβ”€β”€ server/
β”‚   └── websocket-server.ts        # Standalone sync relay
└── data/                          # Created at runtime (gitignored)

Scripts

Command Purpose
npm run dev Start Next.js in development
npm run build Production build
npm run start Run production Next.js server
npm run lint ESLint
npx tsx server/websocket-server.ts Start sync relay (required for cross-device sync)

Limitations (honest scope)

  • Single relay β€” not a multi-node cluster; the WebSocket process is a coordination hub, not consensus (Raft/Paxos).
  • No auth β€” anyone with a document URL can join; suitable for demos, not production multi-tenant security.
  • Plain textarea β€” rich text / comments / permissions are out of scope.
  • Yjs handles text merge β€” the app orchestrates transport, persistence, and UX; conflict-free editing semantics come from Yjs’s CRDT layer.

Acknowledgments