npm package: @sanuabeysekara/richtext-editor
Description: A fully-featured rich text editor with LaTeX equations and image management for React
Author: Sanu Abeysekara
Latest version: 2.0.5

# @sanuabeysekara/richtext-editor

A fully-featured React rich text editor with LaTeX equations and image management. Built from scratch without using any editor libraries.

![npm version](https://img.shields.io/npm/v/@sanuabeysekara/richtext-editor)
![license](https://img.shields.io/npm/l/@sanuabeysekara/richtext-editor)

## Features

### ✨ Text Formatting
- **Bold**, *Italic*, <u>Underline</u>, ~~Strikethrough~~
- Headings (H1, H2, H3)
- Bullet and numbered lists
- Text alignment (left, center, right)
- Hyperlinks
- Undo/Redo

### 📐 LaTeX Equations
- Insert inline mathematical equations
- Live preview while typing
- Powered by KaTeX
- Delete with hover button

### 🖼️ Image Management
- Upload images to your backend
- Inline or block alignment options
- Drag corner handles to resize
- Delete with hover button
- Automatic backend cleanup
- Max 1MB per image

### 👁️ Viewer Component
- Read-only display component
- Matches editor styling
- Responsive design
- No editing capabilities

### 🛠️ Developer Features
- HTML code preview
- Copy to clipboard
- Ref methods for programmatic control
- TypeScript ready (types coming soon)

## Installation

```bash
npm install @sanuabeysekara/richtext-editor
```

**Note:** KaTeX CSS is automatically bundled - no extra imports needed!

## Quick Start

```jsx
import React, { useRef } from 'react'
import { RichTextEditor } from '@sanuabeysekara/richtext-editor'
import '@sanuabeysekara/richtext-editor/dist/style.css'

function App() {
  const editorRef = useRef(null)

  const saveContent = () => {
    const html = editorRef.current.getCleanedContent()
    console.log('Content:', html)
  }

  return (
    <>
      <RichTextEditor 
        ref={editorRef}
        apiUrl="http://localhost:8000"
      />
      <button onClick={saveContent}>Save</button>
    </>
  )
}
```

## Components

### RichTextEditor

The main editing component.

```jsx
import { RichTextEditor } from '@sanuabeysekara/richtext-editor'

<RichTextEditor 
  ref={editorRef}
  apiUrl="http://localhost:8000"
  uploadEndpoint="/images/upload"
  deleteEndpoint="/images"
  initialContent="<p>Start here...</p>"
  onChange={(html) => console.log(html)}
  onImageUpload={(data) => console.log(data)}
  onImageDelete={(id) => console.log(id)}
/>
```

### RichTextViewer

Read-only display component.

```jsx
import { RichTextViewer } from '@sanuabeysekara/richtext-editor'

<RichTextViewer content={cleanedHtml} />
```

## Props

### RichTextEditor Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `apiUrl` | string | `'http://localhost:8000'` | Backend base URL |
| `uploadEndpoint` | string | `'/images/upload'` | Image upload endpoint |
| `deleteEndpoint` | string | `'/images'` | Image delete endpoint |
| `initialContent` | string | `'<p><br></p>'` | Initial HTML content |
| `onChange` | function | `null` | Content change callback |
| `onImageUpload` | function | `null` | Image upload callback |
| `onImageDelete` | function | `null` | Image delete callback |

### RichTextViewer Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `content` | string | `''` | HTML content to display |
| `className` | string | `''` | Additional CSS class |
| `style` | object | `{}` | Inline styles |

## Ref Methods

```jsx
const editorRef = useRef(null)

// Get raw HTML (with editor controls)
const raw = editorRef.current.getContent()

// Get cleaned HTML (production-ready)
const clean = editorRef.current.getCleanedContent()

// Set content
editorRef.current.setContent('<p>New content</p>')

// Clear editor
editorRef.current.clear()

// Get plain text
const text = editorRef.current.getText()

// Focus editor
editorRef.current.focus()
```

## Backend Requirements

The editor requires a backend API with these endpoints:

### Upload Image
```http
POST /images/upload
Content-Type: multipart/form-data

Response:
{
  "success": true,
  "image_id": "uuid",
  "filename": "uuid.jpg",
  "url": "/uploads/uuid.jpg",
  "size": 123456
}
```

### Delete Image
```http
DELETE /images/{image_id}

Response:
{
  "success": true,
  "message": "Image deleted"
}
```

**Backend Example:** A Python FastAPI example is included in the repository.

## Complete Example

```jsx
import React, { useRef, useState } from 'react'
import { RichTextEditor, RichTextViewer } from '@sanuabeysekara/richtext-editor'
import '@sanuabeysekara/richtext-editor/dist/style.css'

function MyEditor() {
  const editorRef = useRef(null)
  const [savedContent, setSavedContent] = useState('')
  const [showPreview, setShowPreview] = useState(false)

  const handleSave = () => {
    const cleaned = editorRef.current.getCleanedContent()
    setSavedContent(cleaned)
    setShowPreview(true)
    
    // Save to your backend
    fetch('/api/save', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ content: cleaned })
    })
  }

  return (
    <div>
      <h1>My Editor</h1>
      
      <RichTextEditor 
        ref={editorRef}
        apiUrl="https://api.mysite.com"
        onChange={(html) => console.log('Changed:', html)}
      />
      
      <button onClick={handleSave}>Save</button>
      
      {showPreview && (
        <div>
          <h2>Preview</h2>
          <RichTextViewer content={savedContent} />
        </div>
      )}
    </div>
  )
}
```

## Styling

Both components come with complete CSS included. No additional styling needed.

To customize, wrap in your own container or use the `className` and `style` props.

## Browser Support

- Chrome/Edge 90+
- Firefox 88+
- Safari 14+

## License

MIT

## Contributing

Issues and pull requests are welcome!

## Support

For issues, questions, or feature requests, please open an issue on GitHub.