Éditeur markdown collaboratif auto-hébergé, compatible Obsidian — Yjs + Hocuspocus + CodeMirror 6.
  • TypeScript 93.4%
  • CSS 3.4%
  • Shell 2.7%
  • JavaScript 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-08 12:06:33 +00:00
.forgejo/workflows ci: add tag-triggered release workflow, bump to 0.1.0 2026-09-15 23:29:11 +00:00
design chore(icones): retire mes doublons fluent--* au profit des icônes Cédric ic_fluent_* 2026-10-05 16:57:11 +00:00
docs docs(spec,plan): fenêtre de paramètres lot 5 — Corbeille et Données personnelles 2026-10-07 21:24:37 +00:00
patches feat(liens): tous les liens passent par le dialogue, une unité pour le curseur 2026-09-30 10:59:38 +00:00
public ajout et amélioration du message reflexion 2026-09-22 12:44:38 +00:00
scripts fix(dev): dev-worktree.sh — setup d'un sujet neuf, doctor muet, stack du script « sans state », interpréteur de conversion 2026-10-07 11:50:30 +00:00
server feat(trash): durée de conservation par projet 2026-10-07 21:37:02 +00:00
spikes/001-univer-sheet nouveau point de départ apres les changement de branche 2026-09-26 17:29:47 +00:00
src feat(settings): sections Corbeille et Données personnelles dans la fenêtre 2026-10-07 21:44:35 +00:00
tests feat(settings): sections Corbeille et Données personnelles dans la fenêtre 2026-10-07 21:44:35 +00:00
.gitignore ajout de .env copy 2026-09-30 12:17:43 +00:00
.oxlintrc.json Initial commit: éditeur prototype + maquettes + cahier des charges 2026-09-02 21:35:32 +00:00
drizzle.config.pg.ts feat(server): add dual-dialect Drizzle schema and DB client factory 2026-09-03 10:01:31 +00:00
drizzle.config.sqlite.ts feat(server): add dual-dialect Drizzle schema and DB client factory 2026-09-03 10:01:31 +00:00
index.html Initial commit: éditeur prototype + maquettes + cahier des charges 2026-09-02 21:35:32 +00:00
LICENSE docs: add MIT license 2026-09-16 03:57:51 +00:00
package-lock.json chore(release): version 0.1.9 2026-10-08 12:06:33 +00:00
package.json chore(release): version 0.1.9 2026-10-08 12:06:33 +00:00
README.md fix(dev): dev-worktree.sh — setup d'un sujet neuf, doctor muet, stack du script « sans state », interpréteur de conversion 2026-10-07 11:50:30 +00:00
ROADMAP.md docs(roadmap): fenêtre de paramètres lot 5 livré 2026-10-07 21:46:38 +00:00
tsconfig.app.json chore(ts): add global.d.ts for CSS imports + include in tsconfig 2026-09-28 12:03:06 +00:00
tsconfig.frontend-tests.json chore(ts): add global.d.ts for CSS imports + include in tsconfig 2026-09-28 12:03:06 +00:00
tsconfig.json feat(collab): connect the frontend to a Yjs document over /collab 2026-09-05 06:48:12 +00:00
tsconfig.node.json Initial commit: éditeur prototype + maquettes + cahier des charges 2026-09-02 21:35:32 +00:00
tsconfig.server.json feat(collab): connect the frontend to a Yjs document over /collab 2026-09-05 06:48:12 +00:00
vite.config.ts feat(dev): gestionnaire de worktree par chantier + proxy vite cible env 2026-09-28 20:57:31 +00:00
vitest.config.ts test(emoji): EmojiPicker — tests rouges (TDD) + @testing-library/react 2026-09-28 12:03:06 +00:00

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the Oxlint configuration

If you are developing a production application, we recommend enabling type-aware lint rules by installing oxlint-tsgolint and editing .oxlintrc.json:

{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "oxc"],
  "options": {
    "typeAware": true
  },
  "rules": {
    "react/rules-of-hooks": "error",
    "react/only-export-components": ["warn", { "allowConstantExport": true }]
  }
}

See the Oxlint rules documentation for the full list of rules and categories.

Plusieurs stacks en parallèle (script dev-worktree.sh)

scripts/dev-worktree.sh gère une stack par cible : master (ce checkout, backend 3002 + vite 5173) ou <sujet> (.worktrees/<sujet>, branche feat/<sujet>, ports suivants).

scripts/dev-worktree.sh setup <sujet>    # branche + worktree + deps + db
scripts/dev-worktree.sh start [cible]    # backend + vite sur les ports de la cible
scripts/dev-worktree.sh stop [cible]     # arrête ce que le script a lancé
scripts/dev-worktree.sh adopt [cible]    # reprend une stack lancée à la main
scripts/dev-worktree.sh status           # toutes les cibles + ports sans state
scripts/dev-worktree.sh doctor           # ENCRYPTION_KEY ↔ dev.db, dérives, orphelins
scripts/dev-worktree.sh prune [--yes]    # worktrees mergés + states périmés

La clé ENCRYPTION_KEY de chaque cible est générée une fois dans .dev-env (gitignoré) et réutilisée : ne pas la régénérer, sinon la clé API OpenRouter enregistrée dans dev.db devient indéchiffrable (doctor le détecte). stop ne tue que les process qu'il connaît ; start n'écrase jamais une stack en cours — il refuse et propose adopt.

Conversion de documents (scripts/convert-worker.py, qui importe markitdown) : start exporte ARDOISE_CONVERT_PYTHON vers l'interpréteur qui a le module — valeur déjà exportée, sinon .venv-convert/ de la cible, sinon celui du checkout principal (un worktree n'a pas de venv propre). preflight affiche l'interpréteur retenu. Créer le venv une fois dans le checkout principal : python3 -m venv .venv-convert && .venv-convert/bin/pip install 'markitdown[docx,xlsx,xls,pptx,pdf]==0.1.7'.

Dépendances patchées

Ce projet patche localement une dépendance (@atomic-editor/editor) via patch-package — voir patches/ et docs/superpowers/specs/2026-09-13-tableaux-wikilinks-sync-design.md. Le patch se réapplique automatiquement à chaque npm install/npm ci (hook postinstall) ; ne lancez jamais l'installation avec --ignore-scripts.

Backend server

The backend is an Express + Drizzle server (server/) providing vaults and files with stable ids, backed by SQLite (dev) or Postgres (prod).

Environment variables

Variable Default Notes
DATABASE_URL ./dev.db A SQLite file path, or a postgres:///postgresql:// URL to use Postgres instead.
VAULTS_ROOT ./vaults Directory where vault file trees are stored on disk.
HOST 127.0.0.1 Interface the server binds to. See the security note below before changing this.
PORT 3001 Port the server listens on.
ALLOW_DEV_AUTH off Set to 1 to allow the x-dev-user header to stand in for a real identity header. Never enable in production.
JWT_SECRET (required, no default) Signing secret for the short-lived WebSocket authentication tokens (Hocuspocus). Generate with openssl rand -hex 32. The server refuses to start without it.
ENCRYPTION_KEY (required, no default) 32-byte hex secret (openssl rand -hex 32) used to encrypt each user's OpenRouter API key at rest (AES-256-GCM). The server refuses to start without it.

First boot

Before starting the server for the first time (or after a schema change), push the schema to your database:

npm run db:push:sqlite   # dev, uses DATABASE_URL or ./dev.db
npm run db:push:pg       # prod, requires DATABASE_URL pointing at Postgres
npx tsx scripts/history-cleanup.ts          # dry-run: scan for orphaned history entries (omit --apply to preview)
npx tsx scripts/history-cleanup.ts --apply  # apply: delete orphaned entries (after reviewing dry-run output)

Then start the server:

npm run dev:server   # local development, restarts on file changes
npm start            # plain run, no file-watching

⚠️ Security note

This server trusts the remote-user HTTP header as the authenticated user's identity, with no further verification. It is only safe to run behind a reverse proxy (YunoHost's SSOwat in production) that is configured to unconditionally overwrite any client-supplied remote-user header on every request — never merely add to it — before forwarding to this server. If a client can set remote-user itself, it can impersonate any user.

The server must never be reachable except through that proxy. HOST defaults to 127.0.0.1 specifically to enforce this — do not change it to 0.0.0.0 or any other externally-reachable interface without the reverse-proxy guarantee described above already in place.

Les imports de fichiers (images, PDF) sont limités à 25 Mo côté serveur. Si l'application tourne derrière un reverse proxy (nginx/YunoHost), son client_max_body_size doit être réglé à au moins 25 Mo, sinon le proxy rejette la requête avant qu'Express ne la voie.