Commit History
-
fix(highlight): auto-detect language for code blocks without explicit lang
Code blocks authored without a language rendered as plain text in both the editor and the published output (Shiki falls back to "text"). Add a shared lowlight-based detector and use it as a fallback in the editor decorations and the publish transformer, so language-less blocks get highlighted identically everywhere - including in already-written articles. Co-authored-by: Cursor <cursoragent@cursor.com>700adac
-
feat(editor): inline author editor with affiliation typeahead and DnD
Simplify the Authors & affiliations modal UX without changing the data schema: - Add/edit authors inline (drop the separate AuthorInlineForm modal). - Attach affiliations via a single typeahead that matches an existing one or creates a new one in one gesture. - Collapse the affiliations list (reorder + rename + URL) behind a disclosure since it is rarely touched. - Replace up/down chevrons with a grip handle and native drag-and-drop reordering for both authors and affiliations. Store gains atomic addAuthor/updateAuthor (create + attach new affiliations in one transaction) and auto-prunes orphan affiliations with 1-based index remapping. The agent tooling and system prompt follow the new author-driven affiliation model. Co-authored-by: Cursor <cursoragent@cursor.com>53c7a9e
-
feat(editor): authors & affiliations manager modal
Replace the undiscoverable inline drag with a single, discoverable modal for the whole byline. From the hero (pencil button, clicking an author, or clicking an affiliation) users open AuthorsManager to add/edit/remove authors and affiliations and reorder both lists via explicit up/down controls. - store: add updateAffiliation/removeAffiliation; removeAffiliation reconciles every author's 1-based references (drop + shift) like moveAffiliation does - extract AuthorInlineForm into its own file, reused as the add/edit form (stacked dialog) inside the manager - affiliations get inline name/URL editing with debounced commits - drop the inline drag-and-drop handlers/styles from the displayed header Co-authored-by: Cursor <cursoragent@cursor.com>005eeff -
feat(editor): drag-and-drop reordering for authors and affiliations
Authors and affiliations in the hero meta header can now be reordered by dragging. Reordering affiliations remaps every author's 1-based affiliation indices in the same Yjs transaction, so superscripts keep pointing at the right institution. Drop affordances (grab cursor + primary-color edge marker) live in the editor-only _hero-editable.css and never leak into the published HTML. Co-authored-by: Cursor <cursoragent@cursor.com>a2d6d01
-
test(editor): extract refocus re-sync into a unit-tested module
Pull the tab-refocus liveness/lock logic out of the Editor effect into a pure createRefocusSync() so it can be exercised without a browser. Add vitest coverage for the four cases it must distinguish: healthy (no lock), disconnected (lock + unlock on synced), dead readyState, half-open socket (lock after the post-forceSync probe), and the offline grace release. The effect now only wires the visibilitychange trigger. Co-authored-by: Cursor <cursoragent@cursor.com>45d6656 -
feat(editor): local-first persistence + robust refocus re-sync
Prevents a stale never-closed tab from clobbering a collaborator's newer content (the core data-loss incident): - Mesh y-indexeddb onto the shared Y.Doc so edits survive refresh / offline, and a client can re-sync its durable local copy back up if the server ever loses or resurrects a stale document. - On tab refocus, force a sync round-trip and hold the editor read-only until it completes, so the user can't type onto a visibly-stale buffer. Detect dead sockets via websocket status, readyState and last-message timestamp (incl. half-open sockets via a post-forceSync probe). Always release after a 4s grace window so offline editing is never trapped (local-first). Co-authored-by: Cursor <cursoragent@cursor.com>fe9b487
-
feat(hero): persistent Edit + Delete buttons on the banner
A filled banner now exposes two buttons in a top-right action cluster: - Edit (PencilLine icon) - opens the Embed Studio on the banner key so authors can iterate on the HTML via the AI agent, just like any other htmlEmbed. Works whether the banner is an image upload, a chart placeholder, or a custom fragment. - Remove (Trash2 icon, danger hover) - same behavior as before. Both buttons are constantly visible instead of fading in on hover: the banner is a first-class authoring surface and a hidden mutation affordance was confusing once you'd seeded the placeholder content. CSS shifts from .hero-banner-remove (single button, opacity-gated) to .hero-banner-actions (cluster) + .hero-banner-action (per-button styling, always opacity:1). Danger variant tints the Remove button red on hover/focus so the destructive action is still distinguishable from the Edit one. Co-authored-by: Cursor <cursoragent@cursor.com>55f5851 -
feat(editor): Iframe embed component for remote URLs
Adds a new atomic component `Iframe` (slash menu icon 🔗) that embeds an arbitrary remote URL via `<iframe src=...>` instead of inlining HTML from the embed store. Use case: integrating widget Spaces (e.g. carbon- tokenization-widgets) without having to download and adapt their HTML to the strict create-html-embed conventions. - Shared component def (atomic, fields: src, title, desc, height, wide) - Custom NodeView with live iframe preview, Settings + Reload buttons, auto-resize when the remote page emits postMessage({type:"embedResize"}) - Publisher transformer emits <figure class="html-embed"> reusing the existing iframe/figcaption markup so styling and figure numbering carry over for free - llms.txt renderer surfaces the URL as a Markdown link - 3 publisher tests covering happy path, empty-src drop, and wide flag Co-authored-by: Cursor <cursoragent@cursor.com>0c69852
-
feat(editor): code block button in the bubble toolbar
Code block was reachable only via the slash menu (`/code`). The bubble toolbar already exposes Bold / Italic / Strike / inline Code / Heading / Quote / Link, so a code block button fits naturally alongside Quote (the other block-level toggle in that row). - Uses lucide `Code2` (chevrons with a slash) to keep it visually distinct from the existing inline `Code` icon next to it. - Active state mirrors the slash-menu behaviour: lights up when the caret is inside an existing `codeBlock` node, so a second click toggles back to a regular paragraph. - Placed between Quote and Link to mirror the slash menu's own order (Quote -> Code block -> Divider). Co-authored-by: Cursor <cursoragent@cursor.com>add3959
-
fix(editor): footer credit links to the editor Space too
The editor's own footer (EditorFooter.tsx) still pointed at the legacy Astro-only static template. Mirror the publisher-side fix and link to the editor Space instead, so authors see the same canonical entry point whether they're editing or reading the published page. Co-authored-by: Cursor <cursoragent@cursor.com>2bed071 -
fix(hero): live-commit on every keystroke, not only on blur
EditableText (the inline text field used for the title, subtitle, description and link labels in the hero) used to maintain a local React draft and only push the value to Yjs on blur or Enter. Net effect from the user's POV: the sync indicator stayed on "Saved" while typing, the doc didn't actually update until focus left the field, and on a slow tab-out or a browser crash mid-edit the work was lost. Felt unsaved. Switch to a 300ms debounce model: - Every onChange schedules a commit. If the user keeps typing, the timer resets - so we still get one Yjs update per "pause" instead of one per keypress. - onBlur and Enter flush immediately (no laggy "wait for the debounce" feel when committing the field by leaving it). - Escape cancels the pending commit and reverts the on-screen draft to the last server-confirmed value. Note that anything already committed during typing is still in the doc - the user gets it back with Yjs undo (Ctrl+Z), same as Notion / Linear. - las…82e43b6 -
feat(editor): WYSIWYG NodeView for HfUser cards
The HfUser block was rendering as the generic AtomicView - a dashed placeholder card with three labelled inputs - which looked nothing like the actual upstream component (avatar + display name + @handle). Authors couldn't tell what the published page would look like without a round-trip through publish. Replace it with a dedicated NodeView that reproduces the exact DOM of `app/src/components/HfUser.astro` in the editor: .hf-user └─ .hf-user__left ├─ .hf-user__avatar (img or "?" placeholder when empty) └─ .hf-user__text ├─ .hf-user__name (display name) └─ .hf-user__row └─ .hf-user__username (@handle) Inline editor: - Revealed when the node is selected, or when `username` is still empty right after insertion (prompts the author to fill at least the handle). - Three rows: Username / Display name / URL. URL placeholder switches to `https://huggingface.co/<username>` once a handle is known, so the user can see they don't need to…7814104
-
chore(release): prep v0.1.0 with LICENSE, env example and agent undo test
- Add LICENSE (CC-BY 4.0) aligned with the upstream research-article-template - Complete backend/.env.example to cover every env var consumed by the backend (PORT, DATA_DIR, ENABLE_PDF, OAUTH_*, SPACE_ID/SPACE_HOST, HF_DATASET_ID, HF_TOKEN, OPENROUTER_API_KEY/MODEL, PUBLISH_BASE_URL) - Extract createAgentBatch() helper from useAgentChat into frontend/src/editor/agent-undo-batch.ts (pure module, no React) and add the P0 5.3.1 test from docs/TESTS.md: 3 chained agent edits (replaceSelection + applyDiff + updateFrontmatter style) revert with a single Cmd+Z, while the previous user edit stays untouched - Refresh README known-debt list to reflect the new coverage Made-with: Cursord63dcfe
-
feat(demo): full showcase polish - mouse cursor, robust selection, HF affiliations
Demo orchestration: - Visible mouse cursor overlay (popover top-layer, MutationObserver to stay above native dialogs) + humanMove/humanClick/humanHover helpers - Yjs Node.js clients for Bob and Carol with awareness cursors and a set of high-level helpers (writeBlockMathAtSlot, setAffiliationUrl, waitForAffiliationCount, etc.) so peer interactions never collide - Pre-bold sync barrier: Alice waits for Bob's block math and Carol's code block to land before doing the bubble-toolbar bold so layout is frozen during the drag selection (no more cross-paragraph selections) - Progressive cursor-driven text selection (visible glide + step-wise setTextSelection) for bold and rephrase actions - Custom rAF-eased scroll-to-top on the actual .editor-scroll container before the hue drag so the banner retint reads on camera Embed Studio: - New architecture.json companion data file (transformer-block layers) seeded alongside model-accuracy.csv, so the agent has something concrete to r…f469961
-
fix(ui,publisher): kill theme/color flickers and translation prompts
- Published page and its banner iframe now inline a synchronous theme bootstrap in <head>, so the first paint already uses the correct data-theme (no dark -> light flicker on navigation). - <meta name="google|robots" content="notranslate"> on both the editor (frontend/index.html) and the published article so Chrome stops proposing to translate the page to French. - CollaborationCursorV3: silence a peer's caret + label (via the .collaboration-cursor--silenced class) when that same peer also broadcasts an agentFocus. Fixes the duplicate "<Name>" + "<Name> agent" labels that stacked while a chat action was active. - Embed Studio preview container pinned to --page-bg and the iframe element forced to background: transparent + color-scheme: light dark, so the preview tracks dark mode instead of falling through to the browser's default white. - App.tsx primaryHue sync: when the Yjs settings map has no primaryHue (fresh state or after reset-article), drop the inline --p…08e4c7a -
feat(editor): embed studio with data files and agent-aware editing
Editor - New Embed Studio panel for AI-assisted chart creation: upload and manage data files (3MB limit), browse them in a dedicated viewer, and iterate on the generated HTML via a chat wired to a dedicated embed agent (embed-chat + embed-system-prompt + embed-tools + tiptap-catalog) that can create, rename and edit embeds. - Tiptap extensions for agent-driven editing: - agent-focus: Yjs-awareness ranges so every peer sees where another user's AI is working ("<Name> agent" label). - agent-highlight: local PM decoration to mark the paragraph the agent is currently rewriting. - agent-rewrite: typewriter-animated range rewrite command used by the rephrase flow. - Rephrase flow now goes through agent-rewrite (typewriter) instead of a plain replace; bold demonstration moved to the floating bubble toolbar above a selection. - SlashMenu reorganised with grouping and dev-only items dropped. - New components: DataFileViewer, FilesSidebar, UploadZone. - …8fc8501
-
feat(frontend): editor refresh (embed studio, comment popover, shiki, top bar, hooks, styles)
- New top bar + publish dialog + sync indicator + mobile TOC sidebar replace the previous floating actions and full sidebar stack - Add EmbedStudio chat panel wired to the backend embed agent (embed-tools + stream handler), with fullBleed iframe mode for banner rendering and double-buffered previews - Swap legacy comments sidebar for a floating CommentPopover anchored to the active thread - Shiki-based code block extension + scroll guard on top of TipTap v3; richer frontmatter settings (hue slider, template variant, SEO) and Editor footer - Shared hooks (useTheme, useEmbedChat, usePublishStatus) and a singleton MutationObserver so all components subscribe to theme changes from the same source - Reorganize CSS into scoped modules (_ui, _footer, _embed, _embed-studio, _hero, _mermaid, _toc, _form, _code, tokens, layout, publisher, print) and drop the dead postcss config - Add frontend vitest suite + tsconfig(+node) + vite-env typings + typed utils/types modules Made-with: Curs…76fc93a -
feat(demo): add trio Playwright orchestration + seed embeds for the pitch video
- backend/demo/trio.ts drives 3 real Chromium sessions (Alice/Bob/Carol) collaborating on a fresh document in under 30s for the demo reel, with human-like typing and kill-leftovers hygiene on every run - Centralize the hand-crafted banner HTML and demo-specific embeds (confusion matrix, memory bar) so the script stays deterministic for recordings - Expose a `load-demo` entrypoint used by the dev-only script to hydrate the editor in one shot Made-with: Cursorf5f4567
-
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: enable PDF generation by adding playwright dependency
- Add playwright as a production dependency in backend - Set PLAYWRIGHT_BROWSERS_PATH in Dockerfile for reliable browser discovery - Remove silent fallback on playwright install to surface errors - Remove non-functional PDF button from editor hero (only relevant in published output) - Respect showPdf frontmatter flag in publisher pipeline Made-with: Cursor8d245e0 -
fix: generate proper footnotes section in published HTML
- Collect all inline footnotes during postProcess and replace with numbered superscript links ([1], [2], etc.) - Append a <section class="footnotes"> with ordered list at the bottom - Footer JS automatically moves this section into the footer area - Add footnote-ref styling (primary color, hover underline) - Make extractBibliographyHtml recursive to find bibliography in nested nodes - Add two demo footnotes to the default article content Made-with: Cursor77b813e -
feat: align component CSS with research-article-template
- Note: left border accent style with color-mix variants (info/success/danger) - QuoteBlock: decorative open-quote mark, large text, author/source footer - Accordion: template-matching border, bg, chevron, open state highlight - Sidenote: muted text, generous padding (matches Sidenote.astro) - Reference: proper spacing and caption styling - Stack: responsive breakpoints matching template (768/1100px) - Tables: zebra stripes, template header bg, vertical dividers - Wide/fullWidth: proper overflow layout in editor - Add PDF download button to hero meta section (using showPdf flag) - Update publisher HTML styles for all components to match template Made-with: Cursor9f7fae9 -
feat: show real HF username and avatar for connected users
- Fetch /api/auth/status on mount to get HF user info (name, avatar) - Replace random names with actual HF fullName and profile picture - Update collaboration cursor to display avatar next to username - Update top bar Chip with avatar component - Sync awareness when user data arrives after initial render - Use deterministic color from username hash instead of random Made-with: Cursor15dff0c -
fix: prevent seed race condition with Y.Map guard and defensive checks
- Add Y.Map("meta").seeded flag to prevent multiple clients from seeding the same empty doc concurrently (Yjs merge duplication) - Set seededRef early to prevent re-entry - Add authors array length check in seedFrontmatter as extra guard Made-with: Cursorce5a372 -
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 -
feat: adopt template CSS architecture, hero/TOC/color alignment
- Copy template foundation CSS (_variables, _reset, _base, _layout, _print) and component CSS (_code, _table, _tag, _card, _mermaid) - Extract hero styles from HeroArticle.astro into shared _hero.css - Extract TOC styles from TableOfContents.astro into shared _toc.css - Create _editor-tokens.css for editor-specific design tokens - Scope _reset.css and _base.css global styles to article area to avoid overriding MUI portals (Dialog, Menu, Tooltip) - Exclude _button.css and _form.css from editor (bare element selectors conflict with MUI components) - Align FrontmatterHero class names with template (.hero, .meta, .meta-container-cell, .authors, .affiliations) - Align publisher HTML renderer class names accordingly - Fix TOC sticky positioning (align-self:stretch + sticky wrapper) - Add hue-based color picker (OKLCH slider like template ColorPicker) synced via Yjs settings, driving both MUI theme and CSS variables - Align editor link styles with template (--primary-color) - Add Po…9b7d64e -
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: replace y-websocket with @hocuspocus /provider for protocol compatibility
The frontend was using y-websocket's WebsocketProvider while the backend uses @hocuspocus/server v3, causing ERR_ENCODING_INVALID_ENCODED_DATA errors on every WebSocket message and resulting in an empty Y.Doc server-side (blank published articles). Switch to @hocuspocus/provider which speaks the native Hocuspocus protocol and sends the auth token through the WebSocket connection. Made-with: Cursorb4ed27f -
fix: resolve TipTap v3 collaboration crash
- Replace @tiptap/extension-collaboration-cursor v2 with custom CollaborationCursorV3 using yCursorPlugin from @tiptap/y-tiptap (same ySyncPluginKey as Collaboration v3) - Simplify CollaborationUndo to read UndoManager from Collaboration v3's yUndoPlugin instead of creating its own - Fix BubbleMenu import: @tiptap/react/menus (v3 API change) - Install @floating-ui/dom, remove y-prosemirror and .npmrc - Fix backend Image import to named export Made-with: Cursorade54e4 -
fix: adapt to StarterKit v3 API changes
StarterKit v3 now includes Link, Underline, and UndoRedo by default. - Remove separate Link import/config, use StarterKit's link option - Disable undoRedo in StarterKit (replaced by CollaborationUndo) - Apply same fix to backend publisher extensions Made-with: Cursoredbe0c1 -
chore: upgrade all TipTap packages to v3
- Bump all @tiptap/* from v2 to v3.22.3 - Keep extension-collaboration-cursor at v2.26.2 (no v3 available) - Add .npmrc with legacy-peer-deps for the collab-cursor compat - Add @tiptap/y-tiptap, @tiptap/extension-bubble-menu, tippy.js - Fix named imports (Image, Table, TableRow, etc.) for v3 API - Fix BubbleMenu import path Made-with: Cursord28f5cf -
refactor: store pre-computed attrs in citation/bibliography nodes
Citation: - Add 'label' attribute to citation extension - CitationView persists computed label back into node attrs - Server-side renderHTML reads label attr for complete output Bibliography: - Add 'renderedHtml' attribute to bibliography extension - BibliographyView persists formatted HTML into node attrs - Server-side renderHTML produces complete bibliography section Footnote: - Server-side renderHTML shows tooltip + footnote marker These pre-computed attributes allow generateHTML() to produce complete HTML without React NodeViews. Made-with: Cursor905f030 -
feat: add client-side image optimization (WebP variants)
- Create image-optimizer.ts: resize images to 3 WebP variants (thumb 400px, medium 1000px, full 2000px) using OffscreenCanvas - Update upload.ts to optimize before uploading to backend - Graceful fallback to original file if optimization unsupported Made-with: Cursor7bafae7 -
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