@danjari/blocknote-ai-extension
v1.0.0
Published
AI-powered slash commands extension for BlockNote editor. Add ChatGPT, Claude, and Gemini AI capabilities to your BlockNote editor with custom slash commands like /explain, /summarize, /quiz-me, and /diagram.
Maintainers
Readme
BlockNote AI Slash Extension
AI-powered slash commands extension for BlockNote editor (use case for Synapsed ). This extension adds AI functionality to BlockNote while maintaining full compatibility with all existing features.
✨ Features
- AI Slash Commands:
/explain,/quiz-me,/summarize,/diagram - Full BlockNote Compatibility: All existing commands work alongside AI commands
- Event-Driven Architecture: Clean separation between UI and AI logic
- Streaming Support: Real-time AI response streaming
- Synapsed Theme: Custom styling with indigo accent colors
- TypeScript Support: Full type safety
🚀 Quick Start
Installation
npm install @danjari/blocknote-ai-extensionWhen to Add AI APIs
Phase 1: Development & Testing (Current)
- ✅ Use simulated responses for testing
- ✅ Focus on UI/UX and command structure
- ✅ Test with the BlockNote playground
- ✅ Verify all commands work correctly
Phase 2: AI Integration (Next Step)
- 🔄 Replace simulated responses with real AI APIs
- 🔄 Add error handling and fallbacks
- 🔄 Implement streaming for real-time responses
- 🔄 Add user authentication and rate limiting
Phase 3: Production (Final)
- 🎯 Deploy with production AI services
- 🎯 Add monitoring and analytics
- 🎯 Implement caching and optimization
- 🎯 Add advanced features like conversation history
Basic Usage
import { BlockNoteEditor, getDefaultSlashMenuItems } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/react";
import {
getAISlashMenuItems,
createAIStreamingUtils,
} from "@danjari/blocknote-ai-extension";
// Create AI command handler
function handleAICommand(action, payload) {
console.log("AI Command:", action, payload);
// Connect to your AI API here
switch (action) {
case "explain":
// Call your AI API for explanation
break;
case "quiz-me":
// Generate quiz
break;
case "summarize":
// Summarize content
break;
case "diagram":
// Create diagram
break;
}
}
// Create editor with AI commands
const editor = new BlockNoteEditor({
slashMenuItems: [
...getDefaultSlashMenuItems(editor), // All original BlockNote commands
...getAISlashMenuItems(editor, handleAICommand), // AI commands
],
});
// Set up streaming utilities
const streamingUtils = createAIStreamingUtils(editor);
// Render the editor
function MyEditor() {
return <BlockNoteView editor={editor} />;
}🎯 Available Commands
Original BlockNote Commands (All Work!)
/paragraph- Regular text block/heading- Heading (H1, H2, H3)/bulletListItem- Bullet list item/numberedListItem- Numbered list item/quote- Quote block/code- Code block/image- Image block/table- Table/divider- Horizontal divider- And many more...
AI Commands (New!)
/explain🤖 - Explain selected text/quiz-me🤖 - Generate quiz based on content/summarize🤖 - Summarize selected text/diagram🤖 - Create diagram from content
🔧 API Reference
getAISlashMenuItems(editor, emitAICommand)
Returns AI slash menu items for the BlockNote editor.
Parameters:
editor- BlockNote editor instanceemitAICommand- Function to handle AI commands
Returns: Array of slash menu items
Example:
const menuItems = getAISlashMenuItems(editor, (action, payload) => {
console.log("AI Command:", action, payload);
});createAIStreamingUtils(editor)
Creates streaming utilities for real-time AI responses.
Parameters:
editor- BlockNote editor instance
Returns: AIStreamingUtils instance
Example:
const streamingUtils = createAIStreamingUtils(editor);
// Start streaming AI response
streamingUtils.startStreaming("quote", "AI is thinking...");
// Update streaming content
streamingUtils.updateStreaming("This is the AI response...");
// Finish streaming
streamingUtils.finishStreaming();useAISlash(config)
React hook for AI functionality.
Parameters:
config- Configuration object (optional)
Returns: AI hook with event emitter
Example:
const aiSlash = useAISlash({
callbacks: {
onAICommand: (event) => {
console.log("AI Command:", event.action, event.payload);
},
onStream: (chunk) => {
console.log("Stream chunk:", chunk);
},
},
});🔗 Complete Integration Example
import { BlockNoteEditor, getDefaultSlashMenuItems } from "@blocknote/core";
import { BlockNoteView } from "@blocknote/react";
import {
getAISlashMenuItems,
createAIStreamingUtils,
useAISlash,
} from "@danjari/blocknote-ai-extension";
function MyAIEditor() {
// Set up streaming utilities
const [streamingUtils, setStreamingUtils] = useState(null);
// AI command handler
const handleAICommand = async (action, payload) => {
console.log("AI Command:", action, payload);
// Start streaming indicator
if (streamingUtils) {
streamingUtils.startStreaming("quote", "AI is thinking...");
}
try {
// Call your AI API
const response = await fetch("/api/ai/" + action, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: payload.selectedText,
action: action,
}),
});
const data = await response.json();
// Update streaming content
if (streamingUtils) {
streamingUtils.updateStreaming(data.content);
streamingUtils.finishStreaming();
}
} catch (error) {
console.error("AI API error:", error);
if (streamingUtils) {
streamingUtils.finishStreaming();
}
}
};
// Create editor
const editor = useMemo(() => {
const editorInstance = new BlockNoteEditor({
slashMenuItems: [
...getDefaultSlashMenuItems(editorInstance), // All original commands
...getAISlashMenuItems(editorInstance, handleAICommand), // AI commands
],
});
// Set up streaming utilities
setStreamingUtils(createAIStreamingUtils(editorInstance));
return editorInstance;
}, []);
return <BlockNoteView editor={editor} />;
}🎨 Theming
The extension includes Synapsed theme with indigo accent colors:
:root {
--bn-synapsed-accent-color: #6366f1;
--bn-synapsed-font-family: "Inter", sans-serif;
}Import the theme CSS:
import "@danjari/blocknote-ai-extension/theme.css";📦 Exports
import {
// Main functions
useAISlash,
getAISlashMenuItems,
createAIStreamingUtils,
AIStreamingUtils,
// Theme
defaultSynapsedTheme,
generateSynapsedThemeCSS,
getDefaultSynapsedThemeCSS,
// Types
AICommandAction,
AICommandPayload,
AISlashConfig,
AISlashHook,
AISlashMenuItem,
SynapsedTheme,
} from "@danjari/blocknote-ai-extension";🧪 Testing
Run the test suite:
npm testTest manually:
# Test extension functionality
node test-extension.js
# Test compatibility with BlockNote
node test-compatibility.js
# Test all commands
node test-all-commands.js
# View integration example
node integration-example.jsBrowser Testing:
Open simple-test.html in your browser to test the extension in a web environment.
Interactive Testing in BlockNote Playground:
Start the BlockNote playground:
cd /path/to/blocknote pnpm run devNavigate to your example:
- Open
http://localhost:5177/ - Look for "Custom AI Slash Extension" in the AI section
- Click to load your extension
- Open
Test your commands:
- Type
/to see the slash menu - Try
/explain,/quiz-me,/summarize,/diagram - Verify responses appear in the editor
- Type
🚀 Deployment
Pre-deployment Checklist:
Build the extension:
npm run buildTest in production environment:
npm run previewVerify all commands work:
- Test all AI commands (
/explain,/quiz-me,/summarize,/diagram) - Ensure BlockNote commands still work
- Check TypeScript compilation
- Test all AI commands (
Test with your AI integration:
- Connect to your AI APIs
- Test response handling
- Verify streaming works
Production Integration:
// Production-ready AI command handler
const handleAICommand = async (action, payload) => {
try {
// Call your production AI API
const response = await fetch("/api/ai/" + action, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AI_API_KEY}`,
},
body: JSON.stringify({
text: payload.selectedText,
action: action,
userId: getCurrentUserId(), // Add user context
sessionId: getSessionId(), // Add session tracking
}),
});
if (!response.ok) {
throw new Error(`AI API error: ${response.status}`);
}
const data = await response.json();
return data.content;
} catch (error) {
console.error("AI command failed:", error);
return `Sorry, I couldn't process that request. Please try again.`;
}
};Environment Variables:
# Required for production
AI_API_KEY=your_ai_api_key_here
AI_API_ENDPOINT=https://api.your-ai-service.com
AI_MODEL=gpt-4 # or your preferred model
# Optional
AI_MAX_TOKENS=1000
AI_TEMPERATURE=0.7
AI_TIMEOUT=30000🤖 AI Integration Guide
Step 1: Choose Your AI Provider
OpenAI (Recommended for beginners):
// OpenAI integration
const openaiResponse = await fetch(
"https://api.openai.com/v1/chat/completions",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-4",
messages: [
{ role: "system", content: "You are a helpful AI assistant." },
{ role: "user", content: `Explain this: ${payload.selectedText}` },
],
max_tokens: 1000,
}),
},
);Anthropic Claude:
// Claude integration
const claudeResponse = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": process.env.ANTHROPIC_API_KEY,
"Content-Type": "application/json",
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: "claude-3-sonnet-20240229",
max_tokens: 1000,
messages: [
{ role: "user", content: `Explain this: ${payload.selectedText}` },
],
}),
});Google Gemini:
// Gemini integration
const geminiResponse = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${process.env.GEMINI_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
contents: [
{
parts: [{ text: `Explain this: ${payload.selectedText}` }],
},
],
}),
},
);Step 2: Implement Command-Specific Logic
const handleAICommand = async (action, payload) => {
const selectedText = payload.selectedText || "current content";
switch (action) {
case "explain":
return await callAI(`Explain this text in simple terms: ${selectedText}`);
case "summarize":
return await callAI(
`Summarize this text in 2-3 sentences: ${selectedText}`,
);
case "quiz-me":
return await callAI(
`Create 3 multiple choice questions based on this text: ${selectedText}`,
);
case "diagram":
return await callAI(
`Create a mermaid diagram based on this text: ${selectedText}`,
);
default:
return "Unknown AI command";
}
};
async function callAI(prompt) {
// Your AI API call here
const response = await fetch("/api/ai", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt }),
});
const data = await response.json();
return data.content;
}Step 3: Add Streaming Support
// For real-time streaming responses
const handleAICommandWithStreaming = async (action, payload) => {
const streamingUtils = createAIStreamingUtils(editor);
// Start streaming indicator
streamingUtils.startStreaming("quote", "AI is thinking...");
try {
// Call your streaming AI API
const response = await fetch("/api/ai/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ action, payload }),
});
const reader = response.body.getReader();
let accumulatedContent = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = new TextDecoder().decode(value);
accumulatedContent += chunk;
// Update streaming content
streamingUtils.updateStreaming(accumulatedContent);
}
// Finish streaming
streamingUtils.finishStreaming();
} catch (error) {
console.error("Streaming error:", error);
streamingUtils.finishStreaming();
}
};Step 4: Error Handling & Fallbacks
const handleAICommandWithFallback = async (action, payload) => {
try {
// Primary AI service
return await callPrimaryAI(action, payload);
} catch (error) {
console.warn("Primary AI failed, trying fallback:", error);
try {
// Fallback AI service
return await callFallbackAI(action, payload);
} catch (fallbackError) {
console.error("All AI services failed:", fallbackError);
// Return helpful error message
return `Sorry, I'm having trouble processing your request right now. Please try again in a moment.`;
}
}
};✅ Compatibility
✅ Full BlockNote Compatibility
- All original slash commands work
- All block types supported
- All formatting options available
- No breaking changes
✅ AI Commands
- Properly grouped under "AI" category
- No conflicts with existing commands
- Follow BlockNote command structure
- Type-safe implementation
🔧 Development
Building the extension:
npm run buildDevelopment mode:
npm run devPreview:
npm run preview📄 License
MIT License - see LICENSE file for details.
🤝 Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
🚀 Roadmap
- [ ] Interactive quiz blocks
- [ ] More AI commands
- [ ] Custom AI model support
- [ ] Advanced streaming features
- [ ] Collaborative AI features
