Plotra Docs
Contributing

Monorepo layout

What lives where in the Plotra repository.

Plotra is one repository using Bun workspaces and Turborepo. Each thing that gets deployed is an app in apps/. Shared code lives in packages/.

plotra/
  apps/
    app/        the product: workspaces, projects, the editor and every view (Next.js)
    collab/     the real-time server: a Cloudflare Worker, one Durable Object per document
    web/        the public site (Next.js)
    docs/       these docs (Next.js + Fumadocs)
  packages/
    db/         @plotra/db: Drizzle schema, migrations, database client, row-level security helpers
    auth/       @plotra/auth: Better Auth configuration, server and client
    ui/         @plotra/ui: shared components (shadcn/ui on Radix) and the Tailwind theme
    icons/      @plotra/icons: the Solar icon component and the subset of icons that ship
    config/     @plotra/config: shared tsconfig
  scripts/      repository scripts (check-licenses.ts)
  plan.md       the project plan: what is built, what was tested, what is next
  turbo.json
  package.json  workspaces: apps/*, packages/*

Rules

  • Apps never import from other apps, only from packages/*.
  • Internal packages ship TypeScript source. Their exports point at src/, and each Next.js app lists them in transpilePackages. There is no build step for packages.
  • Environment variables live with the app that uses them (apps/app/.env.local, apps/collab/.dev.vars, apps/web/.env.local), never at the root.
  • Sign-in exists only in apps/app. The public site links to it; it has no session of its own.

Inside apps/app

app/
  (auth)/                     sign-in, sign-up
  w/[workspace]/              a workspace: its projects and settings
  w/[workspace]/p/[project]/  a project: the shell with binder, panes and inspector
  share/, shared/, invite/    accepting a share or an invitation; projects shared with you
  api/                        auth, collab callbacks, file uploads, exports, reads
components/
  workspace/                  the shell: binder, panes, inspector, command palette, view registry
  editor/                     the Plate editor and its plugins (wiki-links, embeds, script formats, block tags)
  views/                      one file per view: timeline, graph, plot grid, corkboard, ...
  collab/                     the Yjs provider, remote cursors, presence
  workflow/                   comments and version history panels
  world/                      canon, reveals, entity types
lib/
  *-actions.ts                server actions (see "How the app works")
  access/                     who may do what
  reads.ts, read.ts           reads the client polls, outside the server-action queue
  db.ts                       the database connection for user requests
  errors.ts                   messages that are safe to show the user
  quotas.ts, rate-limit.ts    the hosted version's abuse limits
  continuity.ts, horizon.ts, calendar.ts, fountain.ts, ...   pure logic, no React and no database
proxy.ts                      route protection (Next.js 16 calls middleware "proxy")

Tabs and split panes are state, not routes: a project is one page, and what is open in it is stored in the layout.

Views

A view has two parts:

  • its entry in components/workspace/view-meta.ts: id, title, icon, which note kinds it opens, whether it gets a ribbon button, and which module it belongs to;
  • its component, attached in components/workspace/view-registry.tsx.

The metadata file imports no components, so the shell can build commands from it without loading every view. To add a view, add both.

Modules (lib/modules.ts) switch groups of views and commands on and off per project. A view that names a module is only offered while that module is on.

The collab server

apps/collab is small on purpose. It checks a token, syncs Yjs documents and awareness, and stores document state. Everything else is decided by the Next app: who may connect (the token carries the role), and what the text means (the server posts content back to the app, which rebuilds the search text, word counts and the link index).

Rooms are node-<id> for a document and presence-<projectId> for who is in a project. Viewers and commenters get read-only connections.

Icons

Plotra uses Solar icons by 480 Design (CC BY 4.0) through @plotra/icons. Only the icons listed in packages/icons/src/registry.ts are bundled.

To add one:

  1. Add its name to packages/icons/src/registry.ts. Keep the list sorted.
  2. Run bun run icons from the repository root to rebuild the subset.
  3. Use it: <Icon name="book-2-bold-duotone" />.

Style: -linear icons for interface chrome, -bold-duotone for empty states and highlights. Don't add another icon library.

Next.js is version 16

Some of it differs from older versions and from what most tutorials show. The docs that match the installed version are in apps/app/node_modules/next/dist/docs/. Read the relevant page before using an API you are unsure about.

On this page