A lightweight Google-Docs-style collaborative editor. Sign up, create and edit
rich-text documents with debounced autosave, import .txt/.md files, and share
documents by email with view or edit permissions — split into a clear Owned vs.
Shared with me dashboard. Built with Next.js 16 (App Router), React 19, Supabase
(Postgres + Auth + Row-Level Security), Tailwind v4 + shadcn/ui, and Vitest.
Live URL: https://docs-repo-omega.vercel.app · Architecture: docs/ARCHITECTURE.md · AI workflow: docs/AI-WORKFLOW.md
- Auth — email + password (Supabase). Self-serve signup, no seeding.
- Rich-text editor — Tiptap (bold, italic, underline, H1/H2, bullet + numbered lists). Content stored as HTML.
- Debounced autosave (~800ms) with a "Saving… / Saved" pill, a
beforeunloadguard, and flush-on-unmount for in-app navigation. No manual Save button. - Import
.txtand.mdfiles client-side; Markdown is rendered and all imported HTML is sanitized (DOMPurify) before it is stored. - Sharing by email — grant another account view or edit access; access is enforced by Postgres Row-Level Security, not application code.
- Node.js 22 LTS — this repo pins it via
.nvmrc. With nvm-windows:nvm install 22 && nvm use 22. - pnpm — enable via Corepack (ships with Node):
corepack enable pnpm.
pnpm install
# configure environment
cp .env.example .env.local # then fill in the values
pnpm dev # http://localhost:3000See .env.example. You only need the two Supabase keys:
NEXT_PUBLIC_SUPABASE_URL/NEXT_PUBLIC_SUPABASE_ANON_KEY— from your Supabase project's API settings (Project Settings → API).
src/app/api/chat/route.tsis an unused scaffold stub (returns501); it needs no key and is not part of this product.
The schema, trigger, and Row-Level Security policies live in a single migration:
supabase/migrations/0001_init.sql. Run it once
against your project (Supabase dashboard → SQL Editor → paste & run).
Confirm email = OFF. In the Supabase dashboard, set Auth → Providers → Email → Confirm email = off. Signup then returns a live session immediately, which is what lets reviewers create two accounts and test sharing without an email round-trip.
.md— Markdown, rendered to rich text (headings, lists, emphasis, etc.)..txt— plain text; each line becomes a paragraph.
Any other extension is rejected with an inline error. All imported content is
sanitized (DOMPurify) before it is stored, so embedded <script>/HTML cannot execute.
- Open the app and Sign up with any email + password (no confirmation email —
see the note above). Repeat in a second browser/incognito window to make a
second account (e.g.
a@example.comandb@example.com). - As account A: New document → type → watch the pill go "Saving…" → "Saved".
Reload to confirm it persisted. Try Import with a
.mdor.txtfile. - As account A: open a doc → Share → enter account B's email → choose view or edit → Share.
- As account B: the doc appears under Shared with me. With edit access B can change it and A sees the update; with view access B cannot modify it.
pnpm dev— dev serverpnpm build/pnpm start— production build / servepnpm lint— ESLintpnpm test/pnpm test:run— Vitest (watch / single run)pnpm format/pnpm format:check— Prettier
src/
app/
login/page.tsx # login + signup
page.tsx # dashboard RSC — "Owned" / "Shared with me"
doc/[id]/page.tsx # RSC: fetch doc + permissions, render client editor
actions/ # Server Actions (mutations)
auth.ts # signUp / signIn / signOut
documents.ts # create / import / rename / save / delete
shares.ts # shareDocument / unshareDocument
components/
editor.tsx # Tiptap + autosave + beforeunload + flush-on-unmount
editor-toolbar.tsx # bold/italic/underline/H1/H2/lists
share-dialog.tsx # share by email + permission
import-button.tsx # FileReader → parseImportedFile → importDocument
auth-form.tsx
ui/ # shadcn/ui components
lib/
parse-import.ts # parseImportedFile() — the tested pure fn
parse-import.test.ts
supabase/ # client.ts, server.ts, proxy.ts
utils.ts # cn() + shared helpers
proxy.ts # session refresh (Next.js 16 "proxy", formerly middleware)
supabase/migrations/0001_init.sql # schema + trigger + RLS
See docs/ARCHITECTURE.md for the data model, RLS policy design, and the autosave/sharing trade-offs.
See AGENTS.md for the full stack details and the conventions agents
(and humans) should follow — RSC for reads, Server Actions for mutations, Supabase
client usage rules, and the Next.js 16 proxy change.
Deploys to Vercel:
- Import the repo into Vercel (framework auto-detected as Next.js).
- Add the two
NEXT_PUBLIC_SUPABASE_*environment variables in the Vercel project settings (same values as.env.local). - In the Supabase dashboard → Authentication → URL Configuration, add the Vercel deployment URL as the Site URL (and to Redirect URLs).
- Deploy, then smoke-test: sign up → create → autosave → reload → share.
Intentionally out of scope for this slice: real-time collaboration, comments/
suggestions, version history, .docx import, roles beyond view/edit, and a manual
Save button. profiles.email is publicly readable so share-by-email can resolve a
recipient — a documented trade-off (see docs/ARCHITECTURE.md).