Created
September 25, 2025 10:30
-
-
Save kabeer11000/1f1b3f3d6a8d2f11b74504c179794daa to your computer and use it in GitHub Desktop.
Revisions
-
kabeer11000 created this gist
Sep 25, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,266 @@ Creating a Document Editor with AI and delta changes a faster way. ## 1. **Use Tiptap's Transaction API** Instead of replacing the entire document, create transactions that apply specific changes: ```javascript // Your chatbot returns structured commands const chatbotResponse = { message: "I've added a security section and updated the introduction.", edits: [ { type: "insertHeading", position: 150, level: 2, text: "API Security Best Practices" }, { type: "insertParagraph", position: 151, text: "Implement rate limiting and input validation..." }, { type: "replaceText", from: 45, to: 67, text: "comprehensive security protocols" } ] }; // Apply changes using Tiptap transactions function applyEdits(editor, edits) { const tr = editor.state.tr; edits.forEach(edit => { switch(edit.type) { case 'insertHeading': tr.insert(edit.position, editor.schema.nodes.heading.create( { level: edit.level }, editor.schema.text(edit.text) )); break; case 'insertParagraph': tr.insert(edit.position, editor.schema.nodes.paragraph.create( {}, editor.schema.text(edit.text) )); break; case 'replaceText': tr.replaceWith(edit.from, edit.to, editor.schema.text(edit.text)); break; } }); editor.view.dispatch(tr); } ``` ## 2. **Position-Based Updates with Node IDs** Add IDs to your document structure for reliable targeting: ```javascript // Configure Tiptap with node IDs const editor = new Editor({ extensions: [ StarterKit, // Add ID extension for trackable nodes Document.extend({ addGlobalAttributes() { return [ { types: ['heading', 'paragraph', 'bulletList'], attributes: { id: { default: null, parseHTML: element => element.getAttribute('data-id'), renderHTML: attributes => { if (!attributes.id) return {}; return { 'data-id': attributes.id }; }, }, }, }, ]; }, }), ], }); // Chatbot references nodes by ID const edits = [ { type: "updateNodeById", nodeId: "intro-paragraph-1", content: "Updated introduction text..." }, { type: "insertAfterNode", nodeId: "section-2-heading", nodeType: "paragraph", content: "New paragraph after section 2..." } ]; ``` ## 3. **Semantic Section Management** Create helper functions to work with document sections: ```javascript class DocumentManager { constructor(editor) { this.editor = editor; } findNodeById(id) { let foundNode = null; this.editor.state.doc.descendants((node, pos) => { if (node.attrs.id === id) { foundNode = { node, pos }; return false; // stop iteration } }); return foundNode; } insertAfterNode(nodeId, nodeType, content) { const found = this.findNodeById(nodeId); if (!found) return; const tr = this.editor.state.tr; const insertPos = found.pos + found.node.nodeSize; const newNode = this.editor.schema.nodes[nodeType].create( { id: `generated-${Date.now()}` }, this.editor.schema.text(content) ); tr.insert(insertPos, newNode); this.editor.view.dispatch(tr); } updateNodeContent(nodeId, newContent) { const found = this.findNodeById(nodeId); if (!found) return; const tr = this.editor.state.tr; const newNode = found.node.type.create( found.node.attrs, this.editor.schema.text(newContent) ); tr.replaceWith(found.pos, found.pos + found.node.nodeSize, newNode); this.editor.view.dispatch(tr); } } // Usage const docManager = new DocumentManager(editor); // Apply chatbot edits chatbotEdits.forEach(edit => { switch(edit.type) { case 'updateSection': docManager.updateNodeContent(edit.sectionId, edit.content); break; case 'addAfterSection': docManager.insertAfterNode(edit.afterId, edit.nodeType, edit.content); break; } }); ``` ## 4. **Range-Based Updates** For more complex edits, use Tiptap's range utilities: ```javascript import { findChildren } from '@tiptap/core'; function applyRangeEdit(editor, edit) { const { from, to, content, nodeType = 'paragraph' } = edit; const tr = editor.state.tr; // Create new content const nodes = content.map(text => editor.schema.nodes[nodeType].create({}, editor.schema.text(text)) ); // Replace range with new nodes tr.replaceWith(from, to, nodes); editor.view.dispatch(tr); } // Chatbot can specify ranges to replace const edit = { type: "replaceRange", from: 100, to: 250, content: [ "Updated first paragraph...", "New second paragraph..." ] }; ``` ## 5. **Integration with RAG System** Structure your RAG prompt to return Tiptap-compatible edits: ```javascript const prompt = ` You are editing a document using Tiptap editor. Return your response as JSON with: 1. "message": Your conversational response 2. "edits": Array of edit operations Available edit types: - insertAfterNode: {type, afterNodeId, nodeType, content, id} - updateNode: {type, nodeId, content} - replaceRange: {type, from, to, content} - insertAtPosition: {type, position, nodeType, content, id} Current document structure: ${getDocumentStructure()} User request: ${userMessage} `; // Process RAG response async function handleChatbotResponse(userMessage) { const response = await callRAG(prompt); const parsed = JSON.parse(response); // Show conversational response displayChatMessage(parsed.message); // Apply document edits applyEditsToDocument(parsed.edits); } ``` ## 6. **Track Changes for Undo/Redo** Tiptap handles this automatically, but you can add custom tracking: ```javascript // Before applying edits const beforeState = editor.state; applyEdits(editor, chatbotEdits); // Add to custom history if needed addToChangeHistory({ type: 'chatbot-edit', before: beforeState, after: editor.state, timestamp: Date.now() }); ``` This approach lets you make precise, incremental changes to your Tiptap document without regenerating the entire content. The editor's transaction system ensures smooth updates and maintains undo/redo functionality. Would you like me to elaborate on any specific part or help you implement a particular edit type?