Commit History
-
refactor(backend): modular server split with new routes, persistence and agent layer
- Introduce create-app factory to compose routes, middleware and Hocuspocus in isolation - Extract route modules (auth, chat, publish) and move storage logic into persistence.ts - Harden auth + HF storage integration (user token fallback, secure cookie in production) - Add embed studio AI pipeline (embed-chat, embed-tools, system prompt) and shared stream handler - Cover the new surface with api-routes, persistence and agent-chat test suites - Ship .env.example and .gitignore so new contributors can boot without guesswork Made-with: Cursorf6678ab
-
refactor: remove MUI, unify publisher pipeline, add shared component registry
Phase 1 - Publisher pipeline: - Extract ~370 lines of inline CSS from html-renderer.ts into _publisher.css - Create shared/component-defs.ts as single source of truth for component definitions - Replace 6 regex blocks in postProcess() with linkedom DOM manipulation - Add GET /api/preview/:docName for testing renders without publishing - Add 12 snapshot tests for each postProcess transformation (46 tests total) Phase 2 - Remove MUI: - Replace all MUI components with native HTML elements + custom CSS - Create _ui.css with editor chrome styles using --ed-* CSS tokens - Create Tooltip.tsx using Floating UI (lightweight replacement) - Replace @mui/icons-material with lucide-react - Delete theme.ts, remove @mui/* and @emotion/* dependencies Phase 3 - Documentation: - Add docs/ARCHITECTURE.md covering CSS layers, HF Spaces constraints, shared registry, publisher pipeline, and test coverage Made-with: Cursord15d7f7 -
fix: replace undefined __dirname with DATA_DIR in ESM server
__dirname was removed during utils.ts extraction but still referenced in staticDir computation, causing a ReferenceError crash on startup. Made-with: Cursorac48bd2 -
fix: XSS escaping, DRY utils, agent undo batch, graceful shutdown
- Escape meta.licence in published HTML to prevent XSS - Extract shared sanitizeName/docPath/DATA_DIR into utils.ts (DRY) - Disable WS debug logging in production - Fix startup log message (was /collab/:doc, now /collab) - Remove unused NextFunction import in auth.ts - Remove duplicate JSDoc in publisher/index.ts - Add timeout to graceful shutdown (SIGTERM/SIGINT) - Fix agent undo batch: close batch on stream finish (onFinish) - Fix replaceSelection: return error when no text selected Made-with: Cursore371833 -
fix: prevent Y.Doc content duplication from seed race condition
The editor was seeding default content on an 800ms timer that could fire before the Hocuspocus sync completed. When the server had existing content from the HF dataset, Yjs would merge both copies (different client IDs), causing all content, authors, and affiliations to appear multiple times. Fix: only seed after the "synced" event, and check the Y.Doc fragment directly instead of ProseMirror state. Also adds /api/admin/reset-document endpoint to clear corrupted Y.Doc state. Made-with: Cursor1c21ac9 -
fix: robust published article restore on container rebuild
- Add detailed logging to pullPublishedAssets for diagnostics - Retry restore for anonymous visitors (dataset is public, no token needed) - Add mutex to prevent concurrent pull attempts - Add direct HTTP fetch fallback when Hub library fails - Log SPACE_ID and HF_DATASET_ID at startup Made-with: Cursorded4123 -
fix: rename to Research Article Template Editor
Made-with: Cursor1e2f7dc -
refactor: rename to Science Article Template Editor, remove MDX export, fix settings drawer
- Rename project from "Collab Editor" to "Science Article Template Editor" across README, package.json, HTML titles, and docs - Remove MDX export: delete to-mdx.ts, serializers, UI button, and registerComponentSerializers; use editor.getText() for agent context - Settings drawer: flatten Advanced accordion, fix toggle clipping (ml: 0), widen to 380px, use slotProps.paper (MUI v9) Made-with: Cursor20354ec
-
fix: use manage-repos scope, public dataset, remove image proxy
- Change OAuth scope from write-repos to manage-repos (required for dataset creation - this was silently failing before) - Create dataset as public instead of private - Remove token requirement from pullPublishedAssets (public reads) - Remove /api/images/* proxy route (unnecessary with public dataset) Made-with: Cursor56a5607 -
fix: lazy-restore published article using OAuth token
The startup pull fails when there's no HF_TOKEN env var (common in Spaces using OAuth only). Now the restore is attempted: 1. At startup (works if HF_TOKEN secret is set) 2. On first authenticated request (auth/status with user token) 3. On first visit to the public page (if visitor has a cookie) Uses a flag to ensure the pull only runs once successfully. Made-with: Cursor871447f -
feat: restore published article from HF dataset on startup
After a container rebuild, the local data directory is empty. On boot, the server now pulls published assets (HTML, PDF, thumbnail, meta) from the HF dataset back to local storage so the published article is served immediately without needing to re-publish. Made-with: Cursord2072ce -
fix: accept /collab WebSocket upgrade without trailing slash
The Hocuspocus provider connects to /collab (no trailing slash), but the upgrade handler only matched /collab/. Also add debug logging for incoming WS messages and proper error handling. Made-with: Cursor1c0ee53 -
fix: replace express-ws with native ws to fix WebSocket binary handling
express-ws bundles ws@7.5.10 while @hocuspocus/server requires ws@8.x. This version mismatch causes ERR_ENCODING_INVALID_ENCODED_DATA errors as the binary Yjs protocol messages get corrupted passing through express-ws's incompatible ws instance. Replace express-ws with native http.createServer + ws.WebSocketServer handling the upgrade event directly. This ensures a single ws@8.20.0 is used throughout, matching what Hocuspocus expects. Made-with: Cursor82092f2 -
debug: enhance publish pipeline logging
Log live Hocuspocus document state, XmlFragment content, full JSON structure, CSS loading, and meta to diagnose incomplete published articles. Made-with: Cursorf797eff -
fix: pass OAuth cookie token to Hocuspocus WebSocket auth
The WebSocket provider doesn't send the OAuth token in the protocol message. Extract it from the HTTP cookie during the upgrade request and pass it via context to onAuthenticate. Made-with: Cursorf307f28 -
feat: auto-create private HF dataset using OAuth token
- No more need for HF_TOKEN secret; the logged-in user's OAuth token (with write-repos scope) is used for all HF API calls - Dataset ID is derived from SPACE_ID (<space>-data) when not explicit - Dataset is created automatically (private) on first write operation - Last valid OAuth token is cached in memory for background operations (debounced document push) Made-with: Cursor34f4083 -
fix: flush Hocuspocus document to disk before publish
openDirectConnection retrieves the live Y.Doc from Hocuspocus and writes it to data/<doc>.yjs so the publisher always finds the file, even when Hocuspocus hasn't triggered a store callback yet. Made-with: Cursorc49b156 -
fix: use target=_blank for OAuth login (sandboxed iframe)
HF Space iframe doesn't allow top-navigation. Use target="_blank" to open OAuth flow in a new tab as recommended by HF docs. After login, the callback redirects to /editor in that new tab. Made-with: Cursorb6e0911 -
feat: implement proper HF OAuth flow
- Add /oauth/authorize route that builds the correct HF OAuth URL with client_id, redirect_uri, state (CSRF protection) - Add /auth/callback route that exchanges code for access token, stores it in httpOnly cookie, redirects to /editor - Use OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OPENID_PROVIDER_URL env vars injected by HF when hf_oauth: true - isOAuthEnabled now checks for both SPACE_ID and OAUTH_CLIENT_ID Made-with: Cursorbafd8cb -
fix: OAuth login works inside HF Space iframe
- Login link uses target="_top" to break out of iframe - /editor redirect uses window.top.location instead of server redirect Made-with: Cursor6c9458d -
fix: prevent express.static from serving index.html on /
express.static was intercepting GET / and serving the SPA before the route handlers could check auth. Use { index: false } so only JS/CSS/assets are served statically; index.html routing is handled explicitly by /editor and the catch-all. Made-with: Cursor5d44891 -
refactor: remove doc URL parameter, single document per Space
Always use "default" as the document name. One Space = one article. Made-with: Cursorc353d31 -
feat: clean routing - /editor for auth, / for published article
- GET / serves published HTML if available, otherwise login page - GET /editor requires HF OAuth, serves the SPA editor - In dev mode (no SPACE_ID), / serves the editor directly - Cleaner login page with styled "Sign in with HF" button Made-with: Cursor0784389 -
fix: auth flow for publish and write access check
- Add credentials: "include" to publish fetch so cookies are sent - Rewrite checkWriteAccess: check Space owner match or org membership instead of unreliable /settings endpoint probe - Add debug logging for auth and publish failures Made-with: Cursor3616e87 -
feat: add publish pipeline with HTML/PDF/thumbnail generation
Publisher pipeline (POST /api/publish): - Server-side TipTap extensions mirroring frontend (no React) - generateHTML() produces article HTML with SEO meta tags - Playwright-based PDF and OG thumbnail generation (optional) - Upload published artifacts to HF dataset (or local fallback) - HTML template with light/dark theme toggle, image lightbox Routing updates: - Authenticated editors get SPA, visitors get published HTML - Image proxy for private HF datasets (/api/images/*) - Serve local published articles from /published/ Also: add type declarations for citation-js and playwright Made-with: Cursor3171ebe -
feat: add HF OAuth authentication middleware
- Create auth.ts: resolveUser via whoAmI, extractToken from cookie, checkWriteAccess against Space repo settings - Add GET /api/auth/status endpoint returning user info + canEdit flag - Add onAuthenticate hook to Hocuspocus to protect WebSocket - Enable hf_oauth in README.md Space config - Graceful fallback: all users can edit when SPACE_ID is unset Made-with: Cursorb3d2294 -
chore: initial commit
Collaborative article editor with TipTap, Yjs, and HF integration. Features: - Real-time collaborative editing (Hocuspocus + Yjs) - Notion-like block handles, slash commands, bubble toolbar - Academic citations (Citation.js + DOI/BibTeX) - KaTeX math, code highlighting, tables - Custom component system (accordion, note, quote, stack, etc.) - LLM agent sidebar (OpenRouter) - Image upload with HF dataset storage - MDX export - Frontmatter management (title, authors, affiliations) - Comments system Made-with: Cursor561e6f0