Live demo: https://sheet.schnsrw.live/ — auto-deployed from main on every push (CI → Pages).
A web-based, Excel-flavored spreadsheet editor with real-time collaboration on the roadmap. Built on Univer (Apache-2.0 OSS).
The goal: feel like Excel, not like Google Sheets — ribbon, formula bar, file-centric workflow.
Phase 1 — single-user editor, feature-rich and locked down by 129 Playwright tests. Phase 2 — real-time co-editing (Yjs + Hocuspocus + Redis persistence), shipped as a self-hosted Docker image.
- CI runs
lint,typecheck, and the full Playwright suite on every PR andmainpush (.github/workflows/ci.yml). - Deploy publishes
apps/webto GitHub Pages on everymainpush (.github/workflows/deploy-pages.yml). - Tests: 129 e2e specs in
tests/e2e/; xlsx / ods / csv / tsv round-trip, hyperlinks, drag-fill, drag-and-drop file open, formula evaluation, tables, freeze, comments, and more. - Live URL: https://sheet.schnsrw.live/
| Working | Coming |
|---|---|
| Office-style ribbon (Home / Insert / Formulas / Data) | Real-time co-editing |
| Inline cell editing, F2, Backspace, Delete, Escape | Presence (cursors, avatars) |
Formula bar with editable Name Box (type B5 to jump) |
More ribbon tabs (Review / View) |
| Fonts (family + size), colors, fill, wrap, alignment | Charts |
| Borders (split-button dropdown with 7 modes) | Pivot tables |
| Cell merge + unmerge | Recent-files / landing page |
| Group / outline rows + columns (collapse / expand) | WOPI host integration |
| AutoSum / Average / Count / Min / Max | |
| Sort ascending/descending, Filter | |
| Help → Report a bug (prefills env to GitHub issue form) | |
| Styled tooltips on every toolbar / shell button | |
| Open / Save .xlsx / .ods / .csv / .tsv (full round-trip) | |
| Drag-and-drop file open + Save / Export toasts | |
| Hyperlinks (import + export round-trip) | |
| Drag-fill handle, Ctrl+D / Ctrl+R, relative-ref formula extension | |
| Tables (Format as Table, themes, Tables panel) | |
| Comments (with corner indicator markers) | |
| Freeze panes (top row / first column / at selection) | |
| Sheet tabs at the bottom (add / rename / delete / reorder) | |
| Auto-fit column / row | |
| Print active sheet with Page Setup (orientation + margins, prefs persist) | |
| Border color picker (preset swatches + custom color) | |
| Multi-column sort dialog (Data → Sort range…) | |
| File menu with Properties dialog | |
| Dynamic workbook growth — 1024×128 → 8192×1024 | |
| Material Symbols icons, Inter typography |
Single image. Web + Hocuspocus + Fastify in one container, Redis alongside for room persistence so sessions survive restarts.
# Run the published image directly (in-memory rooms — fine for a quick try):
docker run --rm -p 3000:3000 schnsrw/casual-sheets:latest
# open http://localhost:3000
# Or with persistence + Redis via compose:
docker compose up -d
# open http://localhost:3000Co-edit a sheet: open http://localhost:3000/r/<any-id> in two browsers —
both join the same Y.Doc, edits propagate in <1 s. Anonymous, no login.
The same image is built and pushed from this repo. See
docs/CO-EDITING.md for the architecture and
Dockerfile / docker-compose.yml
for the deploy shape.
| Endpoint | What it does |
|---|---|
GET / |
Serves the built web app |
GET /r/:roomId |
Same SPA; bridges into the named Y.Doc |
POST /api/rooms |
Allocates a fresh empty room id |
GET /api/rooms |
Diagnostic — live rooms + client counts |
GET /health |
Liveness probe (returns {ok, ts, rooms}) |
WS /yjs |
Hocuspocus Yjs sync |
| Env var | Default | Meaning |
|---|---|---|
PORT |
3000 |
HTTP + WS port |
HOST |
0.0.0.0 |
Bind address |
REDIS_URL |
unset | If set, Y.Docs persist to Redis with a 7-day TTL |
ROOM_TTL_MIN |
5 |
Minutes a room stays in memory after the last client leaves |
Prereqs: Node ≥ 18.17, pnpm 10+.
pnpm install # one-time
pnpm dev:web # http://127.0.0.1:5273
pnpm dev:server # http://127.0.0.1:3000 (HTTP + Hocuspocus WS at /yjs)
pnpm test:e2e # Playwright (auto-starts the web dev server)
pnpm test:e2e:ui # Playwright UI mode
pnpm lint # eslint
pnpm format # prettier --write
pnpm typecheck # tsc across packages.
├── apps/
│ ├── web/ ← Vite + React frontend
│ └── server/ ← Fastify scaffold (Hocuspocus + xlsx routes land in Phase 2)
├── tests/e2e/ ← Playwright e2e suite
├── docs/
│ ├── ARCHITECTURE.md ← system design
│ └── RESEARCH.md ← Univer technical brief
├── vendor/univer/ ← read-only Univer 0.22.1 source clone (gitignored)
├── PLAN.md ← phased build plan
└── CLAUDE.md ← project guardrails
vendor/univer/ is a local clone of dream-num/univer for source-level study and is excluded from version control. To bootstrap on a fresh checkout:
git clone --depth 1 https://github.com/dream-num/univer.git vendor/univer| Concern | Pick |
|---|---|
| Editor + formula engine | Univer OSS (@univerjs/core + sheets plugins, pinned to 0.22.1) |
| Frontend | React + Vite + TypeScript (strict) |
| Lint / format | ESLint 9 (flat config) + Prettier |
| xlsx I/O | ExcelJS |
| Icons / type | Material Symbols Outlined + Inter (Google Fonts) |
| E2E tests | Playwright (Chromium) |
| Collab transport (Phase 2) | Yjs + Hocuspocus |
Apache-2.0. See LICENSE and NOTICE for attribution.
Vendored Univer source (vendor/univer/) keeps its upstream Apache-2.0 license; we do not modify it and it is not part of our build.