npm package: @jonahschulte/rtf-editor
Description: React WYSIWYG editor for RTF documents with track changes support
Author: Jonah Schulte
Latest version: 0.3.0

# @jonahschulte/rtf-editor

React WYSIWYG editor for RTF documents with track changes support.

## Features

- **WYSIWYG Editing** - Rich text editing powered by Quill
- **Track Changes Support** - Visual display of insertions and deletions
- **Accept/Reject Changes** - Individual or bulk change management
- **Multiple View Modes** - Markup, Final, and Original views
- **Customizable Styling** - Custom colors for track changes
- **Responsive Design** - Works on desktop and mobile

## Installation

```bash
npm install @jonahschulte/rtf-editor
```

## Peer Dependencies

```bash
npm install react react-dom
```

## Usage

### Basic Usage

```tsx
import { useState } from 'react';
import { RTFEditor } from '@jonahschulte/rtf-editor';

function App() {
  const [rtfContent, setRtfContent] = useState('');

  return (
    <RTFEditor
      value={rtfContent}
      onChange={(rtf, html) => setRtfContent(rtf)}
      showSidebar
      showTrackChangesToolbar
    />
  );
}
```

### With Track Changes Callbacks

```tsx
import { RTFEditor } from '@jonahschulte/rtf-editor';

function App() {
  const handleTrackChangesUpdate = (doc, changes) => {
    console.log(`${changes.length} changes remaining`);
  };

  return (
    <RTFEditor
      value={rtfContent}
      onChange={handleChange}
      onTrackChangesUpdate={handleTrackChangesUpdate}
      showSidebar
      showTrackChangesToolbar
    />
  );
}
```

### Custom Colors

```tsx
<RTFEditor
  value={rtfContent}
  onChange={handleChange}
  trackChangesColors={{
    insertionColor: '#e6ffe6',
    deletionColor: '#ffe6e6',
    insertionBorderColor: '#00cc00',
    deletionBorderColor: '#cc0000',
  }}
/>
```

### Read-Only Mode

```tsx
<RTFEditor
  value={rtfContent}
  readOnly
  showSidebar
  defaultViewMode="final"
/>
```

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string` | `''` | RTF content |
| `onChange` | `(rtf: string, html: string) => void` | - | Called when content changes |
| `onTrackChangesUpdate` | `(doc, changes) => void` | - | Called when changes are accepted/rejected |
| `readOnly` | `boolean` | `false` | Disable editing |
| `allowEditingWithChanges` | `boolean` | `false` | Allow editing when track changes exist (not recommended) |
| `placeholder` | `string` | `'Start typing...'` | Placeholder text |
| `showSidebar` | `boolean` | `true` | Show track changes sidebar |
| `showTrackChangesToolbar` | `boolean` | `true` | Show track changes toolbar |
| `defaultViewMode` | `'markup' \| 'final' \| 'original'` | `'markup'` | Initial view mode |
| `className` | `string` | `''` | Custom CSS class |
| `height` | `string \| number` | `400` | Editor height |
| `toolbarOptions` | `array` | - | Custom Quill toolbar options |
| `trackChangesColors` | `object` | - | Custom colors for track changes |

## View Modes

| Mode | Description |
|------|-------------|
| `markup` | Shows all changes with visual styling (green for insertions, red strikethrough for deletions) |
| `final` | Shows the document as if all changes were accepted |
| `original` | Shows the document as if all changes were rejected |

## Read-Only Mode with Track Changes

By default, the editor becomes **read-only when unresolved track changes exist**. This prevents data loss and confusion:

- ✅ Prevents edits from being lost when accepting/rejecting changes
- ✅ Clear workflow: resolve all changes first, then edit the clean document
- ✅ Visual indicator explains why editing is disabled
- ✅ Matches behavior of professional editors like Microsoft Word

**To enable editing:**
1. Accept or reject all track changes to remove them
2. Or set `allowEditingWithChanges={true}` to override (not recommended)

**Why read-only by default?**

When you edit a document with track changes and then accept/reject a change, the entire document structure is regenerated to apply the change. Any edits you made will be lost because they exist only in the HTML layer, not the underlying RTF structure. Read-only mode prevents this confusion.

## Undo/Redo Track Changes

The editor supports undoing and redoing track changes operations (accept/reject):

- **Keyboard shortcuts**:
  - `Ctrl/Cmd + Z` - Undo last accept/reject operation
  - `Ctrl/Cmd + Shift + Z` - Redo last undone operation
- **Toolbar buttons**: Undo and Redo buttons appear in the track changes toolbar when available
- **Automatic**: Undo stack is cleared when loading new documents
- **Scope**: Only affects accept/reject operations, not content editing (which uses Quill's built-in undo)

## Components

The package also exports individual components for custom layouts:

```tsx
import {
  RTFEditor,
  TrackChangesSidebar,
  TrackChangesToolbar
} from '@jonahschulte/rtf-editor';
```

## Known Limitations

### Lossy HTML-to-RTF Conversion

When editing content in the WYSIWYG editor, the `onChange` callback provides both RTF and HTML. However, the HTML-to-RTF conversion is **lossy** and will not preserve:

- **Track changes metadata** (insertions, deletions, author info, timestamps)
- **Custom fonts** (defaults to Times New Roman)
- **Color tables**
- **Revision tables**
- **Other RTF-specific formatting**

This is because:
1. The editor uses Quill (HTML-based) for WYSIWYG editing
2. Quill doesn't understand RTF-specific concepts like track changes
3. The `convertHTMLToRTF` function creates a new RTF document from scratch

**Recommendations:**
- Use this editor primarily for **viewing** RTF documents with track changes
- For editing, consider storing the HTML output separately
- If you need to preserve the original RTF, keep a copy and only use the HTML for display
- Accept/reject track changes through the UI before editing content

### Debounced onChange

The `onChange` callback is debounced (500ms) to prevent performance issues during rapid typing. This means:
- Changes are not emitted on every keystroke
- The callback fires 500ms after the user stops typing
- This is intentional to avoid re-render loops

## Related

- [@jonahschulte/rtf-toolkit](https://github.com/jschulte/rtf-toolkit) - Core RTF parsing and rendering library

## License

MIT