import { $generateNodesFromSerializedNodes } from '@lexical/clipboard' import { BaseSerializedNode } from '@lexical/clipboard/clipboard' import { InitialEditorStateType } from '@lexical/react/LexicalComposer' import { $getRoot, $insertNodes, LexicalEditor } from 'lexical' import { Pencil, Search, Trash2 } from 'lucide-react' import { Notice } from 'obsidian' import React, { useCallback, useEffect, useRef, useState } from 'react' // import { v4 as uuidv4 } from 'uuid' import { lexicalNodeToPlainText } from '../../components/chat-view/chat-input/utils/editor-state-to-plain-text' import { useDatabase } from '../../contexts/DatabaseContext' import { DBManager } from '../../database/database-manager' import { TemplateContent } from '../../database/schema' import LexicalContentEditable from './chat-input/LexicalContentEditable' export interface QuickCommand { id: string name: string content: TemplateContent createdAt: Date | undefined updatedAt: Date | undefined } const CommandsView = ( { selectedSerializedNodes }: { selectedSerializedNodes?: BaseSerializedNode[] } ) => { const [commands, setCommands] = useState([]) const { getDatabaseManager } = useDatabase() const getManager = useCallback(async (): Promise => { return await getDatabaseManager() }, [getDatabaseManager]) // init get all commands const fetchCommands = useCallback(async () => { const dbManager = await getManager() dbManager.getCommandManager().getAllCommands((rows) => { setCommands(rows.map((row) => ({ id: row.id, name: row.name, content: row.content, createdAt: row.createdAt, updatedAt: row.updatedAt, }))) }) }, [getManager]) useEffect(() => { void fetchCommands() }, [fetchCommands]) // new command name const [newCommandName, setNewCommandName] = useState('') // search term const [searchTerm, setSearchTerm] = useState('') // editing command id const [editingCommandId, setEditingCommandId] = useState(null) const nameInputRefs = useRef>(new Map()) const contentEditorRefs = useRef>(new Map()) // 为每个正在编辑的命令创建refs const commandEditRefs = useRef, contentEditableRef: React.RefObject }>>(new Map()); // 获取或创建命令编辑refs const getCommandEditRefs = useCallback((id: string) => { if (!commandEditRefs.current.has(id)) { commandEditRefs.current.set(id, { editorRef: React.createRef(), contentEditableRef: React.createRef() }); } // 由于之前的if语句确保了值存在,所以这里不会返回undefined const refs = commandEditRefs.current.get(id); if (!refs) { // 添加保险逻辑,创建一个新的refs对象 const newRefs = { editorRef: React.createRef(), contentEditableRef: React.createRef() }; commandEditRefs.current.set(id, newRefs); return newRefs; } return refs; }, []); // 当编辑状态改变时更新refs useEffect(() => { if (editingCommandId) { const refs = getCommandEditRefs(editingCommandId); if (refs.editorRef.current) { contentEditorRefs.current.set(editingCommandId, refs.editorRef.current); } } }, [editingCommandId, getCommandEditRefs]); // new command content's editor state const initialEditorState: InitialEditorStateType = ( editor: LexicalEditor, ) => { if (!selectedSerializedNodes) return editor.update(() => { const parsedNodes = $generateNodesFromSerializedNodes( selectedSerializedNodes, ) $insertNodes(parsedNodes) }) } // new command content's editor const editorRef = useRef(null) // new command content's editable const contentEditableRef = useRef(null) // Create new command const handleAddCommand = async () => { const serializedEditorState = editorRef.current.toJSON() const nodes = serializedEditorState.editorState.root.children if (nodes.length === 0) { new Notice('Please enter a content for your template') return } if (newCommandName.trim().length === 0) { new Notice('Please enter a name for your template') return } const dbManager = await getManager() dbManager.getCommandManager().createCommand({ name: newCommandName, content: { nodes }, }) // clear editor content editorRef.current.update(() => { const root = $getRoot() root.clear() }) setNewCommandName('') } // delete command const handleDeleteCommand = async (id: string) => { const dbManager = await getManager() await dbManager.getCommandManager().deleteCommand(id) } // edit command const handleEditCommand = (command: QuickCommand) => { setEditingCommandId(command.id) } // save edited command const handleSaveEdit = async (id: string) => { const nameInput = nameInputRefs.current.get(id) const currContentEditorRef = contentEditorRefs.current.get(id) if (!currContentEditorRef) { new Notice('Please enter a content for your template') return } const serializedEditorState = currContentEditorRef.toJSON() const nodes = serializedEditorState.editorState.root.children if (nodes.length === 0) { new Notice('Please enter a content for your template') return } const dbManager = await getManager() await dbManager.getCommandManager().updateCommand(id, { name: nameInput.value, content: { nodes }, }) setEditingCommandId(null) } // handle search const handleSearch = (e: React.ChangeEvent) => { setSearchTerm(e.target.value) } // filter commands list const filteredCommands = commands.filter( command => command.name.toLowerCase().includes(searchTerm.toLowerCase()) || command.content.nodes.map(lexicalNodeToPlainText).join('').toLowerCase().includes(searchTerm.toLowerCase()) ) const getCommandEditorState = (commandContent: TemplateContent): InitialEditorStateType => { return (editor: LexicalEditor) => { editor.update(() => { const parsedNodes = $generateNodesFromSerializedNodes( commandContent.nodes, ) $insertNodes(parsedNodes) }) } } return (
{/* header */}

Create Quick Command

Name
setNewCommandName(e.target.value)} className="infio-commands-input" />
Content
{/* search bar */}
{/* commands list */}
{filteredCommands.length === 0 ? (

No commands found

) : ( filteredCommands.map(command => (
{editingCommandId === command.id ? ( // edit mode
{ if (el) nameInputRefs.current.set(command.id, el) }} />
) : ( // view mode
{command.name}
{command.content.nodes.map(lexicalNodeToPlainText).join('')}
)}
)) )}
) } export default CommandsView