Edra Editor Documentation

repository·next·Indexed 20 days ago

https://github.com/tsuzat/edra

A block-styled rich text editor for Svelte 5 built using Tiptap. Edra provides a headless core and a styled Shadcn UI version, featuring Notion-style drag-and-drop, slash commands, and AI streaming capabilities. It includes a comprehensive command registry for text formatting, media, math, and diagrams, as well as built-in AI prompt templates for tasks like summarizing, grammar fixing, and writing expansion.

Tokens
13.1K
Snippets
46
Records
62
Agent score
70%

What's inside Edra

  1. Quick Start: Implement the Edra Editor in Svelte 5

    next

    To use Edra, install it via the shadcn-svelte registry, then use createEditor to instantiate the editor instance and the <Edra> component to render the UI.

    Edra provides several sub-components for building the interface:

    • <Edra.Toolbar />: The editor toolbar.
    • <Edra.BubbleMenu />: A floating menu that appears when text is selected.
    • <Edra.Content />: The main editable area.
    • <Edra.DragHandle />: Provides Notion-like drag-and-drop block mechanics.
    • <Edra.UseAI />: Enables AI-driven features.

    You can provide an onUpdate callback to handle content changes and a callAI function to implement your own AI provider for streaming completions.

    <script lang="ts">
    	import { createEditor, Edra } from '$lib/edra/shadcn/index.js';
    	import { updateInDB } from '$lib/db';
    
    	const onUpdate = () => {
    		const content = editor?.getJSON();
    		updateInDB(content);
    	};
    
    	async function callAI(
    		prompt: string,
    		onChunk: (chunk: string) => void,
    		onError: (error: Error) => void
    	) {}
    
    	const editor = createEditor({
    		onUpdate,
    		callAI
    	});
    </script>
    
    <div class="rounded-lg border">
    	<Edra {editor}>
    		<Edra.UseAI />
    		<Edra.Toolbar class="max-w-full! scrollbar-none overflow-x-scroll border-b p-1" />
    		<Edra.BubbleMenu />
    		<Edra.Content class="h-150 cursor-auto overflow-y-scroll px-8 py-4 *:outline-none" />
    		<Edra.DragHandle />
    	</Edra>
    </div
  2. Install Edra Editor via shadcn-svelte

    next

    Edra is distributed via the shadcn-svelte registry. This installs the source code directly into your project, allowing for full control over styling and dependencies. Run the following command to add Edra to your project:

    For the unstyled Headless variant or detailed installation options, consult the official Installation Documentation.

    npx shadcn-svelte@latest add https://edra.tsuzat.com/r/edra.json
  3. How SelectAcrossAtoms handles atom nodes

    next

    The extension identifies 'selectable' atom nodes that should be merged into a TextSelection during drag-selection. Supported nodes include:

    • image
    • blockMath or inlineMath
    • video, audio, or iframe
    • Any node where isAtom is true and it is a leaf node or lacks inlineContent.

    Selection Logic:

    1. Clicking: A left-click on an atom node triggers a NodeSelection.
    2. Entering: If a drag-selection enters an atom node with sufficient penetration depth (defined by ATOM_SLIGHT_PENETRATION_PX), the entire atom is merged into the TextSelection.
    3. Exiting:
      • If the user exits the atom on the same side they entered, the atom is excluded from the selection.
      • If the user crosses to the opposite side, the atom remains included in the selection.
    4. Right-Click: The extension preserves the current selection during a right-click to prevent the browser or ProseMirror from collapsing the selection, ensuring context menus can be used without losing the highlighted range.
  4. Understand the AI's operating modes and formatting rules

    next

    The AI assistant in Edra operates using four distinct modes based on the detected intent of the [USER_PROMPT]. Understanding these modes helps you predict how the AI will respond to your instructions.

    Operating Modes

    1. Architect Mode (Structure & Planning): Triggered by requests for outlines or brainstorming. Uses hierarchical Markdown (headers, bullets).
    2. Coder Mode (Development): Triggered by requests for code or bug fixes.
      • Default: Code + brief explanation.
      • Constraint "Just code": Code block only.
      • Constraint "Explain this": Code with heavy inline comments.
      • Constraint "Refactor/Fix": Corrected code + diff-style summary.
    3. Scholar Mode (Learning & Math): Triggered by requests for definitions or complex explanations. Uses LaTeX for math.
    4. Editor Mode (Refining): Triggered by requests to summarize, expand, or change tone of [SELECTED_TEXT].

    Formatting Standards

    • Math: Use $$ for inline math and $$$ for math blocks.
    • Mermaid Diagrams: Use the following syntax for direct insertion:
      :::mermaid
      content
      :::
    • Style: The AI follows a "No Fluff" policy, avoiding conversational filler (e.g., "Sure, here is...") and diving straight into the content. It mirrors your existing note style (bullets vs. paragraphs).
  5. Use the EdraCommand interface properties

    next

    The EdraCommand interface provides several hooks for integrating with the editor lifecycle:

    • name: A unique identifier for the command.
    • icon: A Lucide icon component used for UI rendering.
    • tooltip: A string displayed when hovering over the command.
    • shortCut: An optional keyboard shortcut string (e.g., ⌘B).
    • onClick: A function called when the command is triggered, receiving the Editor instance.
    • turnInto: A function used to transform an existing node at a specific position into a different node type.
    • isActive: A predicate function used to determine if the command's state is currently active (e.g., if text is bold).
    • clickable: A predicate function to determine if the command should be enabled/clickable in the UI.
  6. Install the SelectAcrossAtoms Tiptap extension

    next

    The SelectAcrossAtoms extension is a Tiptap extension designed to improve selection behavior when interacting with 'atom' nodes (like images, math formulas, or videos) during drag-selection. It allows these nodes to be merged into a TextSelection rather than forcing a NodeSelection, providing a smoother user experience for selecting text across media elements.

    import { Editor } from '@tiptap/core';
    import { SelectAcrossAtoms } from './path-to/SelectAcrossAtoms';
    
    const editor = new Editor({
      extensions: [
        SelectAcrossAtoms,
        // ... other extensions
      ],
    });
  7. Configure media uploads in the Edra editor

    next

    The Edra editor includes a MediaPlaceholder extension that handles file uploads via the onFileUpload prop passed to createEditor. When a user drags/drops, pastes, or selects a media file (image, video, audio), the editor will trigger this callback.

    To ensure media works correctly, your onFileUpload implementation must return a Promise<string> that resolves to the publicly accessible URL of the uploaded file.

    const editor = createEditor({
      onFileUpload: async (file: File) => {
        const url = await myUploadService.upload(file);
        return url; // Must be a public URL
      }
    });
  8. Configure the Edra editor instance with createEditor()

    next

    The createEditor function initializes the editor instance. It accepts an options object with the following properties:

    • onUpdate: A callback function triggered when the content changes. This is useful for saving content to a database. It can access the editor instance via editor?.getJSON().
    • callAI: An asynchronous function used to implement custom AI providers. It receives a prompt and provides onChunk and onError callbacks to handle streaming responses directly into the editor canvas.
    const editor = createEditor({
    	onUpdate: () => {
    		const content = editor?.getJSON();
    		// save content
    	},
    	callAI: async (prompt, onChunk, onError) => {
    		// implement AI provider logic
    	}
    });
  9. Configure the Video Tiptap extension

    next

    The Video extension can be customized via the VideoOptions object when initializing the extension. Key configuration options include:

    • inline: Determines if the video is an inline or block element. Defaults to false.
    • allowBase64: If true, allows base64 encoded video URLs in the src attribute. Defaults to false.
    • HTMLAttributes: A record of HTML attributes to be added to the <video> element. Defaults to {}.
    • resize: Enables resizing for the video node. If provided as an object, it configures the resizing behavior:
      • enabled: Boolean to activate resizing.
      • directions: An array of ResizableNodeViewDirection (e.g., top, right, bottomLeft) defining where resize handles appear.
      • minWidth: Minimum width in pixels.
      • minHeight: Minimum height in pixels.
      • alwaysPreserveAspectRatio: Boolean to force aspect ratio preservation during resize.
    Video.configure({
      inline: true,
      allowBase64: true,
      HTMLAttributes: { class: 'my-video' },
      resize: {
        enabled: true,
        directions: ['top', 'right', 'bottom', 'left'],
        minWidth: 100,
        minHeight: 100,
        alwaysPreserveAspectRatio: true
      }
    })