A TipTap extension for actionable inline content suggestions, comparisons, and diff review workflows
npm install @buddhima_a/tiptap-diff-suggestionsA TipTap extension for inline comparison of content with suggested revisions, including interactive controls.
This extension provides a way to visualize and interact with suggested changes within a TipTap editor. It bridges the gap between static diff viewers and actionable content editing, allowing users to evaluate, accept, or reject suggestions directly in the editor.
> ## Content
> - Use Cases
> - Features
> - HTML Element Structure
> - Installation
> - Usage
> - Basic Setup
> - Inserting diff Suggestions
> - Set editor content with diff Suggestions
> - Advanced Configuration with Side-effects
> - Custom Action Override
>
> - API Reference
> - Styling
> - Examples
> - Development
> - Contributing
>
- AI-Powered Writing Assistance: Displaying and managing suggestions from AI writing tools.
- Collaborative Content Review: Facilitating editorial suggestions and peer review workflows.
- Translation and Localization: Presenting and integrating translation suggestions.
- 📝 Interactive Diff Visualization: Inline comparison with accept/reject controls
- 🎯 Customizable Actions: From simple buttons to complex approval workflows
- 💬 Contextual Comments: Add explanations and reasoning to suggestions
- 🎨 Headless Design: No built-in styles - from quick prototyping to fully branded experiences
- 🔧 Extensible Toolbar: Custom action panels and interactive elements
- 🎭 CSS Variables: Themeable styling system with dark mode support
- 🚀 Framework Agnostic: Leverages TipTap's framework-agnostic architecture
- 📦 TypeScript Ready: Full type definitions for better developer experience
- ⚡ Side-effect Hooks: Integrate with external systems and workflows
The extension generates the following HTML structure:
``html`
Original text
Suggested text
`bash`
npm install @buddhima_a/tiptap-diff-suggestions
`typescript
import { Editor } from '@tiptap/core';
import { DiffSuggestion } from '@buddhima_a/tiptap-diff-suggestions';
const editor = new Editor({
extensions: [
DiffSuggestion,
// ... other extensions
],
});
`
> #### ⚠️ Headless Architecture
> Following TipTap's headless architecture, this extension includes no built-in styling.
> For rapid prototyping, use the included sample CSS. For production, leverage CSS variables and custom styling. Refer styling for more information
`typescript`
editor.commands.insertDiffSuggestion({
id: 'unique-suggestion-123',
comment: 'Suggested improvement for clarity'
});
`typescript
editor.commands.setContent(
This is some random text before suggestion
The old text
The improved text
the text follows suggestions.);`
`typescript
import { DiffSuggestion } from '@buddhima_a/tiptap-diff-suggestions';
const editor = new Editor({
extensions: [
DiffSuggestion.configure({
HTMLAttributes: {
class: 'my-custom-diff',
},
className: 'custom-diff-wrapper',
showButtons: true,
buttons: {
accept: 'Accept',
reject: 'Reject',
},
// Side-effect callbacks
onAccept: (meta) => {
console.log('Accepted suggestion:', meta);
sidebar.highlight(meta.id);
},
onReject: (meta) => {
console.log('Rejected suggestion:', meta);
sidebar.remove(meta.id);
},
}),
],
});
`
`typescript`
DiffSuggestion.configure({
// Full logic override
handleAccept: ({ tr, dispatch }) => {
dispatch(tr.insertText("Custom Accept Logic"));
return true;
},
handleReject: ({ tr, dispatch }) => {
dispatch(tr.insertText("Custom Reject Logic"));
return true;
},
});
- insertDiffSuggestion(options) - Insert a diff suggestionacceptDiffSuggestion(id?)
- - Accept a specific suggestion (or current selection)rejectDiffSuggestion(id?)
- - Reject a specific suggestion (or current selection)acceptAllDiffSuggestions()
- - Accept all suggestions in the documentrejectAllDiffSuggestions()
- - Reject all suggestions in the document
`typescript`
interface DiffSuggestionOptions {
HTMLAttributes: Record
className?: string;
showButtons?: boolean;
buttons?: {
accept?: string;
reject?: string;
};
onAccept?: (meta: DiffSuggestionMeta) => void;
onReject?: (meta: DiffSuggestionMeta) => void;
handleAccept?: Command;
handleReject?: Command;
}
`typescript`
interface DiffSuggestionAttributes {
id: string;
comment?: string;
}
When using side-effect callbacks, you receive a meta object:
`typescript`
interface DiffSuggestionMeta {
id: string;
comment?: string;
accepted: boolean;
originalText: string;
suggestedText: string;
}
The extension follows TipTap's headless approach - no styles are included by default, giving you complete control over the appearance.
For rapid prototyping and development:
`html`
The sample CSS includes CSS variables for easy theming:
`css
:root {
--diff-suggestion-border: #3b82f6;
--diff-suggestion-bg: rgba(59, 130, 246, 0.1);
--diff-old-bg: rgba(239, 68, 68, 0.2);
--diff-new-bg: rgba(34, 197, 94, 0.2);
--diff-accept-btn: #10b981;
--diff-reject-btn: #ef4444;
--diff-toolbar-bg: white;
--diff-toolbar-shadow: rgba(0, 0, 0, 0.1);
}
/ Dark mode variables are also included /
[data-theme="dark"] {
--diff-toolbar-bg: #1f2937;
--diff-toolbar-shadow: rgba(0, 0, 0, 0.3);
/ ... more dark mode variables /
}
`
Target these selectors for custom styling:
- span[data-diff-suggestion] - Main containerspan[data-diff-suggestion-old]
- - Original text styling span[data-diff-suggestion-new]
- - Suggested text styling.diff-suggestion-action-container
- - Action buttons container[data-diff-suggestion-toolbar-accept]
- - Accept button[data-diff-suggestion-toolbar-reject]
- - Reject button
`css
span[data-diff-suggestion] {
border: 2px solid var(--diff-suggestion-border, #3b82f6);
border-radius: 8px;
padding: 4px 8px;
background: var(--diff-suggestion-bg, rgba(59, 130, 246, 0.1));
}
[data-diff-suggestion-toolbar-accept] {
background-color: var(--diff-accept-btn, #10b981);
color: white;
padding: 4px 8px;
border-radius: 4px;
border: none;
cursor: pointer;
}
`
Since the extension expects HTML in the specified structure, you can work with it directly:
`typescript
// Assuming you have diff HTML from your diff algorithm
const diffHTML =
teh quick
the quick
;
// Insert the HTML directly
editor.commands.insertContent(diffHTML);
`
`typescript`
// Create suggestion programmatically
editor.commands.insertDiffSuggestion({
id: Date.now().toString(),
comment: 'AI suggested improvement'
});
For additional usage scenarios and implementation samples, refer to the examples in the repository.
`bash`
npm run build
`bash`
npm run type-check
`bash``
npm run dev
MIT
Contributions are welcome! Please feel free to submit a Pull Request.