--- title: Research Article Template Editor emoji: ✏️ colorFrom: purple colorTo: blue sdk: docker app_port: 8080 pinned: false hf_oauth: true hf_oauth_scopes: - manage-repos - inference-api --- # Research Article Template Editor A collaborative, real-time editor for web-native scientific articles. It lets multiple authors co-write a paper with rich text, math, citations, figures and interactive D3 embeds, then publishes the result as a static HTML page (or a PDF) aligned with the [research-article-template](https://github.com/huggingface/research-article-template). ## What it gives you - **Real-time collaboration** over WebSocket (Y.js + Hocuspocus), with visible cursors and per-user selection colors - **Rich article authoring**: headings, lists, tables, code blocks with syntax highlighting, LaTeX math (KaTeX), footnotes, sidenotes, block quotes, callouts - **Research-specific blocks**: citations + bibliography (BibTeX), figures with captions, stacks / wide / full-width layouts, glossary terms, Mermaid/Wardley/architecture diagrams - **Interactive D3 embeds** authored inline: each embed is a self-contained HTML file the editor can generate and iterate on via an **AI-assisted "embed studio"** - **Comments & discussion** anchored on any selection - **Slash menu** (`/`) and drag/drop block handles, in the spirit of Notion - **Click-to-edit frontmatter**: title, subtitle, authors, affiliations, links, banner color - **Publishing pipeline**: one-click export to a standalone static HTML bundle, plus PDF generation (Puppeteer) and an `llms.txt` Markdown twin for LLM agents/crawlers (served at `/llms.txt`, advertised in `/robots.txt`) - **Persistence**: - Local mode: documents stored on disk under `DATA_DIR` - HF mode: documents pushed/pulled from a Hugging Face dataset via OAuth - **Dark mode**, responsive layout (TOC drawer on mobile), live table of contents with scroll-spy - **AI chat side-panel** that can edit the article via structured tool calls (agent loop over the current TipTap doc) ## Stack | Layer | Tech | |---|---| | Editor | React 18, TypeScript, TipTap v3, ProseMirror | | Collaboration | Y.js, Hocuspocus (WebSocket), y-tiptap | | Backend | Node.js, Express, Vite (dev proxy), Hocuspocus server | | Publishing | Custom TipTap-JSON → HTML renderer, Puppeteer for PDF | | AI | Vercel AI SDK v6 (`ai`, `@ai-sdk/react`) → Hugging Face Inference Providers (OpenAI-compatible router) | | Styling | Plain CSS with custom properties, no framework | | Storage | Local FS or Hugging Face datasets (via `@huggingface/hub`) | | Container | Single-image Docker build, runs on port 8080 | Around **3.6k LOC backend** and **9.5k LOC frontend** (TypeScript/TSX, excluding generated code). ## Repo layout ``` collab-editor/ ├── backend/ # Express + Hocuspocus server, publisher, AI agent routes │ └── src/ │ ├── server.ts # Entry point │ ├── create-app.ts # App factory (routes, middleware, Hocuspocus) │ ├── publisher/ # TipTap-JSON → HTML + PDF │ ├── agent/ # LLM agent (tool calls over the doc) │ ├── shared/ # Component defs shared with the frontend │ └── hf-storage.ts # HF dataset sync ├── frontend/ # Vite + React + TipTap editor │ └── src/ │ ├── App.tsx # Top-level shell │ ├── editor/ # TipTap editor + extensions + components │ ├── components/ # Shared UI pieces (TOC, Chat, Dialog, ...) │ ├── hooks/ # React hooks (agent chat, selection, ...) │ ├── styles/ # CSS layers (see docs/ARCHITECTURE.md) │ └── utils/ ├── docs/ │ ├── ARCHITECTURE.md # Deep dive on layers, data flow, CSS │ ├── SPECIFICATION.md # Feature spec and contracts │ ├── TESTS.md # Testing strategy │ └── embed-studio.md # How the AI-authored embeds pipeline works └── Dockerfile # Production multi-stage build ``` See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for a diagram and the full tour. ## Getting started ### Prerequisites - Node.js 20+ - A Hugging Face token with the `Make calls to Inference Providers` permission for the AI features (embed studio, chat agent). Generate one at https://huggingface.co/settings/tokens. On a HF Space the logged-in user's OAuth token is used instead - no manual setup needed. - A Hugging Face OAuth app (client id/secret) if you want login + HF dataset persistence ### Local development Backend and frontend run as two separate processes in dev (Vite proxies `/api`, `/collab`, `/uploads`, `/published`, `/oauth`, `/auth` to the backend). ```bash # terminal 1 — backend (Express + Hocuspocus on :8080) cd backend cp .env.example .env # set HF_TOKEN, optional OAUTH_* and HF_DATASET_ID npm install npm run dev # terminal 2 — frontend (Vite on :5678) cd frontend npm install npm run dev ``` Then open http://localhost:5678. Open a second tab or browser to see collaboration in action. ### Production (Docker / HF Spaces) The `Dockerfile` builds both frontend and backend into a single image listening on port 8080. This is the image used by the Hugging Face Space. ```bash docker build -t collab-editor . docker run -p 8080:8080 --env-file backend/.env collab-editor ``` Then open http://localhost:8080. ### Run your own copy on a Hugging Face Space Want your own editor? One step: 1. **Duplicate the Space.** On https://huggingface.co/spaces/tfrere/research-article-template-editor, click `⋯ → Duplicate this Space`. Pick your namespace and visibility. HF copies the Dockerfile, the OAuth wiring and rebuilds the image automatically. That's it. No API key to wire up. The AI features (chat agent + embed studio) call **Hugging Face Inference Providers** at `https://router.huggingface.co/v1` using the OAuth token of whoever is currently logged in. As long as your duplicated Space requests the `inference-api` scope (already declared in the README frontmatter as `hf_oauth_scopes`), every editor gets AI for free under their own Inference Providers quota. Optional public variable: `HF_INFERENCE_MODEL` (e.g. `meta-llama/Llama-3.3-70B-Instruct`) to override the default model id. The full list of supported chat-completion models lives at https://huggingface.co/models?inference_provider=all&other=conversational. ## Scripts ### Backend (`cd backend`) | Command | What it does | |---|---| | `npm run dev` | Start Express + Hocuspocus in watch mode | | `npm run build` | Compile TypeScript to `dist/` | | `npm start` | Run the compiled server | | `npm run test` | Unit + integration tests (Vitest) | | `npm run test:e2e` | End-to-end tests (Playwright) | ### Frontend (`cd frontend`) | Command | What it does | |---|---| | `npm run dev` | Start Vite dev server on :5678 | | `npm run build` | Production bundle to `dist/` | | `npm run preview` | Preview the built bundle | | `npm run test` | Unit tests (Vitest) | | `npm run typecheck` | `tsc --noEmit` on the whole frontend | ## Environment variables Copy `backend/.env.example` to `backend/.env` and fill the relevant values. Key ones: | Variable | Purpose | |---|---| | `OAUTH_CLIENT_ID` / `OAUTH_CLIENT_SECRET` | HF OAuth app for user login (required to edit when running on a Space) | | `OAUTH_SCOPES` | OAuth scopes (default `openid profile`). Add `manage-repos` for dataset persistence and `inference-api` to power the AI features with the user's token | | `HF_TOKEN` | Server-side Hugging Face token. Used as a fallback when no user OAuth token is present (e.g. local dev). Needs the `Make calls to Inference Providers` permission to enable the chat agent + embed studio | | `HF_INFERENCE_MODEL` | Override the default chat-completion model id (defaults to `openai/gpt-oss-120b`). Any tool-calling-capable model exposed by HF Inference Providers works | | `HF_DATASET_ID` | Target HF dataset repo for document persistence (when not running on a Space) | | `SPACE_ID` / `SPACE_HOST` | Auto-set by HF Spaces; drive dataset id + secure cookies in production | | `DATA_DIR` | Where documents, uploads and published bundles are stored on disk (default: `./data`) | | `PUBLISH_BASE_URL` | Absolute base URL used when publishing (defaults to `http://127.0.0.1:${PORT}`) | | `ENABLE_PDF` | Set to `false` to disable Playwright-based PDF export | | `PORT` | Server port (default 8080) | ## Testing - **Backend unit tests**: Vitest covers the publisher (HTML renderer, frontmatter, bibliography), storage, auth utilities. - **Backend E2E**: Playwright drives the full editor against a real backend. - **Frontend unit tests**: Vitest covers chat persistence and a handful of utilities. - **Type checking**: `npm run typecheck` in both workspaces. See [`docs/TESTS.md`](docs/TESTS.md) for the current strategy and gaps. ## Known technical debt These are tracked explicitly so new contributors don't trip on them: - **`useEmbedChat` still lacks dedicated unit tests**; the rest of the stores (frontmatter, comments, embeds) and the agent undo batching primitive are now covered. - **Bundle size warning**: the frontend bundle is over the 500 kB Vite warning threshold. Code-splitting the Mermaid / KaTeX / D3 stacks via dynamic imports would help. - **`addToolOutput` typing**: the ai-sdk v6 `ChatAddToolOutputFunction` is a generic over the tool name union. We currently cast to a plain signature at the two call sites because we don't export a typed tool registry yet. - **`backend/src/publisher/html-renderer.ts` is ~1000 LOC**: a per-node-type registry would make it more maintainable. ## License Follow the upstream [research-article-template](https://github.com/huggingface/research-article-template) license.