Commit History
-
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 -
fix(editor): use explicit class for multi-count avatar z-index
Replace the :has() selector with an explicit --multi modifier class for reliable cross-browser support of the overlapping collaborator avatars. Co-authored-by: Cursor <cursoragent@cursor.com>6b2bf82
-
feat(editor): dedupe connected collaborators by identity
The presence avatars were keyed by Yjs awareness clientId, so the same person showed up once per connection: every extra tab/device, and every lingering pre-timeout socket left behind by a reconnect (amplified by the refocus re-sync), rendered the same circle again. Collapse awareness clients sharing an identity (HF username, falling back to avatar URL then clientId) into a single avatar, with a "xN" badge + tooltip when a person has multiple live sessions. Co-authored-by: Cursor <cursoragent@cursor.com>a0b96b1 -
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 -
fix(editor): preview the banner correctly in the Embed Studio
The studio built every preview srcdoc without fullBleed and let the iframe fill the whole preview pane, so the banner (an edge-to-edge 5:2 embed) was rendered like a height-reporting chart and looked nothing like the hero. When editing banner.html, build the srcdoc with `fullBleed: true` and render it inside a centered 5:2 stage (matching .hero-banner dimensions), so the studio preview is a true WYSIWYG of the published header. Regular charts are unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>3d7f9ad -
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
-
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(auth): surface canEdit in the topbar with an access badge
`/api/auth/status` already returned a `canEdit` flag derived from the HF whoami roleInOrg (Space owner, or org write/admin/contributor with a write resource group), but the frontend was ignoring it. Result: read-only users saw an enabled Publish button + the regular user chip, then hit a confusing 403 from the backend on every mutating action. - App.tsx plumbs `canEdit` from /api/auth/status into a new state and forwards it to TopBar. - TopBar renders a small badge next to the user chip: "Editor" (green outline + PencilLine icon) when canEdit, "Read-only" (muted + Lock icon) otherwise, both with a tooltip explaining the state. - Publish button is disabled when !canEdit with a matching tooltip, so the read-only restriction is visible up front instead of after a failed network round-trip. - `.chip--editor` and `.chip--readonly` variants added to _ui.css. Co-authored-by: Cursor <cursoragent@cursor.com>0e7d2a3
-
fix(toc): scope the hide-on-mobile rule to the desktop TOC only
The previous rule hid every `.table-of-contents` on narrow viewports, which also matched the React component re-rendered inside `.toc-mobile-sidebar` (the hamburger drawer). Result: the drawer opened to an empty body. Scope the hide to `.content-grid .table-of-contents` so the drawer copy of the same component stays visible. Co-authored-by: Cursor <cursoragent@cursor.com>bf08f85 -
feat(storage): first-class data - no silent failures in the persistence pipeline
The "Saved" label in the top bar used to be a comforting lie: several failure modes (createRepo 403 on a missing manage-repos scope, writeFileSync on a readonly FS, uploadFile mid-debounce network blip) all collapsed into a console.error that the editor never surfaced. A user could type for an hour, see "Saved" the whole time, lose their tab, and discover only after the container rebuilt that nothing had ever made it to the dataset. Make every link in the chain observable, and make the worst signal always win in the UI. Backend ------- - In-memory `StorageStatus` tracker in hf-storage.ts: - `datasetReady` (createRepo succeeded or returned 409) - `lastLocalSaveAt` / `lastCloudPushAt` epoch ms - `pendingPush` (a debounce timer is armed) - `lastError {stage, message, statusCode, at, docName}` where `stage` is one of `dataset-create | local-save | cloud-push` Every write path records success; every error path records the failure. Success on one stage clears the error for …7a42df5
-
feat(editor): show connected collaborators in the top bar
The collab cursors already render inline next to each peer's selection, but if no one's actively editing right now you have no way to see who else is in the room. Surface that in the top bar as an overlapping row of small avatars (24px circles, -6px overlap, max 4 visible + "+N" badge for the tail), placed just before the SyncIndicator. Data source: the same Y.js awareness states that CollaborationC ursorV3 already populates with \`{ name, color, avatarUrl }\` for every client. We listen to awareness "change" events and re-derive the list on each tick, excluding our own clientID so the user doesn't show up to themselves (they already see their own chip on the far right). Implementation notes: - Border color of each avatar mirrors the user's collab color (same hue as their cursor caret in the doc) so it's obvious at a glance which avatar maps to which inline cursor. - Fallback to a colored initial when avatarUrl is missing (Anonymous sessions before HF OAuth resolves). - Sort by…aa09c01 -
feat(publisher): editor pill shows who you're signed in as
Upgrade the editor quick-link from a bare pencil icon to a proper pill that contextualises the action: ┌──────────────────────────────────────────────────────┐ │ [avatar] Signed in as @tfrere [✎ Edit article] │ └──────────────────────────────────────────────────────┘ The visitor sees who they're authenticated as before clicking, which makes the "would I be writing to the real file?" question self-answering. Useful when multiple HF accounts share the same browser session, or when a collaborator wants to confirm they're not editing on someone else's behalf. Implementation: - Render placeholder content (\"@you\" + empty <img>) so the layout doesn't shift when /api/auth/status comes back. The pill is still \`hidden\` by default - no flash for anonymous visitors. - Inline script fills the <img>.src (preferring user.avatarUrl, falling back to /api/users/<name>/avatar) and the @handle, then drops the \`hidden\` attribute. - New \`.edit-pill\` styles: rounded pill, pa…2b90d0d -
feat(publisher): editor quick-link for signed-in editors
The published article and the editor share the same Space, but a visitor who already has edit rights had no way to jump from one to the other without retyping \`/editor\` in the URL bar. Add a small pencil icon top-right, mirroring the existing theme toggle on the top-left, that links to /editor. Visibility: - Hidden by default (\`<a id="edit-link" ... hidden>\`) so non- editors / anonymous visitors never see a flash. - An inline script probes \`/api/auth/status\` on load and removes the \`hidden\` attribute when \`canEdit\` is true. Fetch failures (offline preview, exported HTML opened from disk, etc.) are silently swallowed - the button just stays hidden. - Hidden under 1100px (same breakpoint as the theme toggle) so mobile / tablet layouts aren't cluttered. Styling: matches the theme toggle (40px circle, page-bg fill, 1px border, soft shadow) so the page stays balanced visually. \`mix(--primary-color)\` tint on hover hints at the destination. Snapshot updated to reflect…44626d0 -
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 -
feat(embed-studio): clickable chart list to switch files
The sidebar already lists every chart in the doc but only the current one was highlighted - the others were inert <li>s. Make non-current entries clickable so the user can jump between charts without going back to the article and reopening another embed. - FilesSidebar: optional onSelectChart prop. Non-current items render as a <button class="es-files__item-main"> (same look as the clickable data files), the current item stays a plain <li> with the "editing" badge. - EmbedStudio: forwards onSelectChart to the sidebar. - App: rotates embedStudioSession when switching, so the studio remounts and useEmbedChat's mount-only loadMessages picks up the chat history persisted for the newly-selected file. - CSS: existing .es-files__item--data { padding: 0 } pattern extended via :has() to any .es-files__item that contains a button child, so the <li> padding doesn't stack with the button's own padding. Co-authored-by: Cursor <cursoragent@cursor.com>79008fb
-
fix(publisher): render HF user card via dedicated transformer
The hfUser atomic node was serialized to an empty `<div data-component="hfUser">` placeholder with no transformer to turn it into the actual profile card, so it rendered as nothing in published articles. Add `hfUserTransformer` mirroring the upstream `HfUser.astro` (avatar + name + @handle), register it in the pipeline, ship the matching `.hf-user` styles in `_publisher.css`, and cover the three cases (full attrs, fallback to username, missing username) with unit tests. Made-with: Cursorf621e1d
-
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
-
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: TOC mobile toggle not visible due to CSS cascade order
The _toc.css component file (loaded last) set display:none on .toc-mobile-toggle/backdrop/sidebar by default, overriding the @media responsive rules in _layout.css (loaded earlier). Move the responsive display overrides into _toc.css so they appear after the default rules and win in the cascade when the media query matches. This fixes the TOC being completely invisible on viewports < 1100px (including HF Spaces iframe at ~968px). Made-with: Cursorc68749f -
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: double TOC in published view and broken collapsible sections
- Add missing mobile TOC styles (display:none defaults, sidebar panel, backdrop, toggle button) extracted from TableOfContents.astro - Add overflow:hidden + transition for toc-collapsible sub-sections Made-with: Cursor9ebbb3c -
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
-
refactor: split editor.css into tokens/article/editing layers
Split monolithic editor.css (1077 lines) into three CSS files: - tokens.css: light/dark design tokens as CSS custom properties - article.css: content styles shared with published version - editing.css: editor-only chrome (cursors, slash menu, panels) All hardcoded dark-mode colors replaced with var() references. Light theme tokens defined and ready for theme toggle. Made-with: Cursor5ad12dd