メインコンテンツまでスキップ

Visual Editor Implementation Plan

Overview

Replace the current plain textarea editor with a GitBook-style visual (WYSIWYG) block editor using Plate (Slate.js-based). The new editor will provide slash commands, drag-and-drop blocks, inline formatting toolbar, and real-time visual rendering — while outputting standard Markdown/MDX compatible with Docusaurus.

Current State

  • Library: @uiw/react-md-editor v4.0.11 (only MDEditor.Markdown used for preview)
  • Editor: Plain <textarea> with monospace font — no toolbar, no syntax highlighting
  • Views: Edit (raw text) / Preview (rendered markdown) / Docs View (iframe)
  • Key file: src/components/Editor/MarkdownEditor.tsx

Library Selection: Plate

Candidates Evaluated

LibraryGitBook-like UXMDX/Markdown FidelityDev EffortMaturity
Plate (Slate.js)Build it (plugins)Native MDX serializationMediumHigh
BlockNote (Tiptap)Built-inLossy markdownLowPre-1.0
Tiptap (ProseMirror)Build it allCustom workHighestHighest
MDXEditor (Lexical)No (traditional WYSIWYG)Native MDX + admonitionsLowestMedium
Milkdown (ProseMirror)Build it allLossless markdownHighMedium

Why Plate

  1. Native MDX serialization@platejs/markdown converts custom elements to MDX tags, which Docusaurus renders directly
  2. Plugin ecosystem — 50+ plugins: tables (resizable), code blocks, slash commands, drag-and-drop, floating toolbar
  3. Notion/GitBook-like UX — achievable by combining slash menu + drag-and-drop + block selection plugins
  4. Docusaurus admonitions — custom plugin can handle :::note, :::tip, :::warning, :::danger, :::info
  5. Front matter — structured metadata panel (sidebar or top section)
  6. MIT license — compatible with BUSL-1.1
  7. React-native — fits our React 19 + TypeScript stack
  8. shadcn/ui components — modern, customizable UI

Why Not Others

  • BlockNote: Closest to GitBook UX out of the box, but markdown conversion is lossy. Every custom block needs manual serialization — risky for Docusaurus .md/.mdx round-trips. Also pre-1.0 (API instability).
  • Tiptap: Most stable foundation (ProseMirror), but requires building everything from scratch — block UX, markdown serialization, MDX support. Highest effort.
  • MDXEditor: Best MDX support (Lexical-based), admonitions and front matter built-in, but not block-based — traditional WYSIWYG, no drag-drop/slash commands.
  • Milkdown: Lossless markdown round-tripping, remark ecosystem, but bare-bones React integration — entire UI must be built manually.

Implementation Phases

Phase 1: Foundation — Replace textarea with Plate editor

Goal: Basic visual editing with markdown round-tripping.

  1. Install dependencies

    npm install @udecode/plate @platejs/basic-nodes @platejs/markdown
    npm install @platejs/heading @platejs/list @platejs/link
    npm install @platejs/horizontal-rule @platejs/autoformat
  2. Create src/components/Editor/PlateEditor.tsx

    • Basic Plate editor with plugins: paragraph, heading (h1-h6), bold, italic, strikethrough, inline code, link, blockquote, ordered/unordered list, horizontal rule
    • Markdown deserialization: file content (string) → Plate value (Slate nodes)
    • Markdown serialization: Plate value → markdown string
    • Props: value, onChange, readOnly
  3. Create src/components/Editor/PlateEditor.module.css

    • Editor styling with Infima CSS variables
    • Light/dark mode support
  4. Modify src/components/Editor/MarkdownEditor.tsx

    • Replace <textarea> with <PlateEditor> in Edit mode
    • Keep Preview mode (MDEditor.Markdown from @uiw/react-md-editor)
    • Keep Docs View mode (iframe, unchanged)
    • Wire dirty state: onChange from PlateEditor → unsaved indicator
    • Save: serialize Plate value → markdown string → send to API
  5. Keep @uiw/react-md-editor — still needed for Preview mode rendering

Phase 2: Rich Block Types

Goal: Full documentation editing capabilities.

BlockPluginSerialization
Tables@platejs/tableStandard markdown table syntax
Code blocks@platejs/code-blockFenced code blocks (```lang)
Images@platejs/media![alt](path) markdown
AdmonitionsCustom plugin:::note\ncontent\n::: (Docusaurus syntax)
Front matterCustom componentYAML front matter (structured editor panel)
  • Admonition plugin (src/components/Editor/plugins/admonition-plugin.ts): Visual block with colored left border + icon for note, tip, warning, danger, info
  • Front matter panel: Parse YAML on load, show as editable form fields (title, sidebar_position, etc.), serialize back on save

Phase 3: Block Editor UX (GitBook-like)

Goal: Intuitive block manipulation experience.

FeaturePluginDescription
Slash commands@platejs/slash-commandType / to open block insertion menu
Drag-and-drop@platejs/dndBlock handles for reordering
Floating toolbar@platejs/floating-toolbarText selection shows formatting options
Block selection@platejs/block-selectionClick handle to select entire blocks
Markdown shortcuts@platejs/autoformat# → heading, * → list, ``` → code block

Slash menu categories:

  • Text: Paragraph, Heading 1-3
  • Lists: Bullet list, Ordered list
  • Media: Image, Code block
  • Layout: Table, Horizontal rule
  • Docusaurus: Note, Tip, Warning, Danger, Info (admonitions)

Phase 4: Polish

Goal: Production-ready editor experience.

  1. Dark mode — CSS variables integration with Infima [data-theme='dark']
  2. Keyboard shortcuts — Ctrl+B (bold), Ctrl+I (italic), Ctrl+K (link), Ctrl+S (save)
  3. Undo/Redo — Plate built-in history plugin
  4. Read-only modereadOnly prop when user lacks write permission
  5. Performance — Lazy loading of heavy plugins, virtualization for large docs

Files to Create/Modify

FileAction
src/components/Editor/PlateEditor.tsxCreate — Main Plate editor component
src/components/Editor/PlateEditor.module.cssCreate — Editor styles
src/components/Editor/plugins/admonition-plugin.tsCreate — Docusaurus admonition block
src/components/Editor/plugins/frontmatter-plugin.tsCreate — Front matter handling
src/components/Editor/MarkdownEditor.tsxModify — Replace textarea with PlateEditor
package.jsonModify — Add Plate dependencies

Critical Requirements

  1. Lossless round-tripping: Edit → Save → Reopen must preserve all content
  2. Front matter preservation: YAML metadata must survive edit cycles
  3. Admonition syntax: :::type blocks must serialize correctly for Docusaurus
  4. MDX compatibility: Custom components in .mdx files should not be corrupted
  5. Existing features preserved: Preview mode, Docs View, build integration, Ctrl+S save

Verification Checklist

  • Existing .md/.mdx files load correctly in the visual editor
  • Edit content visually → Save → Reopen → Content preserved (no data loss)
  • Admonitions (:::note etc.) render as visual blocks and serialize back correctly
  • Front matter preserved through edit cycles
  • Tables, code blocks, images round-trip correctly
  • Slash menu works, drag-drop reorders blocks, floating toolbar formats text
  • Dark mode styling consistent with rest of the app
  • Read-only mode disables editing when user lacks write access
  • npm run typecheck passes
  • npm run build succeeds
  • Preview and Docs View modes still work

References