# Tessera — working in this repo Claude Code loads this file in every session in this repo. **Tessera** is an open-source (MIT), local-first, self-hostable knowledge app: Notion-style blocks and databases, Obsidian-style `[[wikilinks]]`, backlinks and a graph view, real-time collaboration, a sandboxed plugin system, desktop apps and one-command self-hosting. It works fully offline, imports from Notion and Obsidian, and exports to plain markdown at any time. The bar: fast, calm and polished, and it never loses data. ## Where things are | Path | What | | --- | --- | | `packages/core` | Contracts: types, the document schema, `FeatureModule` and extension points, the runtime, stub services | | `packages/ui` | Components, design tokens, `t()` | | `packages/editor` | The block editor (TipTap on Yjs) | | `packages/sync`, `apps/server` | Storage, sync, collaboration, the server (Hocuspocus, SQLite) | | `packages/db-views` | Databases: query engine, views, formulas, CSV | | `packages/search` | Search index, palette, backlinks, graph | | `packages/plugins`, `packages/plugin-api`, `packages/create-tessera-plugin` | Plugin host, SDK, scaffolder (`examples/plugins`, `examples/plugin-template`) | | `packages/markdown`, `packages/importers` | Markdown codec, importers and exporters | | `packages/testkit` | Seeded workspaces, the benchmark harness, Playwright fixtures, CI policy tests | | `apps/web` | The app shell; `src/features//index.ts` registers each feature | | `apps/desktop` | The Tauri desktop app | | `docs/` | The VitePress site (its own pnpm project and lockfile) | | `e2e/`, `scripts/`, `.github/` | End-to-end specs, tooling (see `scripts/README.md`), CI and releases | Read `SPEC.md` for the architecture, contracts and performance budgets. - `HANDOFF/.md` records how each area was built and why. - `HANDOFF/integration.md` and `HANDOFF/polish.md` record the merge, the launch checks and the known gaps. - `agents/` holds the prompts of the parallel build, as history. - The backlog lives in GitHub issues, labeled by area (`area: editor`, …). ## Architecture rules - **Plug in, don't patch.** The shell loads each feature through the `FeatureModule` exported from `apps/web/src/features//index.ts`. Routes, commands, panels, page bodies, block renderers, services and settings are registered there, not wired into the shell or another feature. - **Keep feature folders thin.** `apps/web/src/features//` holds only the registration. Components and logic live in the area's package, with its dependencies in that package's `package.json`. - **`packages/core` is the contract.** Change it deliberately: update `SPEC.md` in the same change, keep the stub implementations working, and update every implementation that the change affects. - **Globs, not lists.** The build, tests and e2e pick up new packages and `e2e/` folders by themselves; root configs rarely need an edit. - **Dependencies:** prefer what's installed. A new one is small, maintained, MIT/Apache-2.0/BSD/ISC, pinned to an exact version, and added to the package that uses it. Say why in the commit. ## Engineering standards - TypeScript `strict`. No `any` (use `unknown` and narrow). No `@ts-ignore` or `eslint-disable` without a comment explaining why. - Tests test real behavior: Vitest for units, Playwright for user flows. Never skip, weaken or delete a test to make it pass. A bug fix comes with a test that failed before it. - No placeholder code in finished work. Unfinished work goes in a GitHub issue, not behind a `TODO`. - UI uses components and design tokens from `packages/ui`, works in light and dark themes, is fully keyboard-accessible (visible focus, correct ARIA via Radix primitives), respects `prefers-reduced-motion`, and works at phone width. - All user-facing strings go through `t()` from `packages/ui`. - Every async UI has loading, empty and error states. Destructive actions are undoable or confirmed. - Security: sanitize any external HTML with DOMPurify, never render unsanitized HTML, never use `eval` or `new Function`, and validate data at trust boundaries with zod. Text from files, the clipboard, other users or the network is untrusted: no regex whose running time grows faster than its input (CodeQL checks for it). - Performance: lazy-load heavy features with dynamic `import()` and respect the budgets in `SPEC.md` §10. - Document state lives in Yjs. Never mirror document content into React state. ## Commands `pnpm dev` · `pnpm build` · `pnpm test` · `pnpm test:e2e` · `pnpm typecheck` · `pnpm lint` · `pnpm format` - `pnpm dev` runs the web app (http://localhost:5173) and the server (port 8787, data in `apps/server/data`) together; the app reaches the server through Vite's `/api` and `/sync` proxy. `pnpm --filter @tessera/web dev` runs the app alone. - `pnpm build` builds every package, the web app, the server and, when Rust is installed, the desktop app (`tauri build --no-bundle`; `TESSERA_SKIP_DESKTOP=1` skips it; installers: `pnpm --filter @tessera/desktop build:app`). - One package: `pnpm --filter @tessera/