'use client' import React, { useCallback, useEffect, useRef } from 'react' import { useEditor, EditorContent, type Editor } from '@tiptap/react' import StarterKit from '@tiptap/starter-kit' import Table from '@tiptap/extension-table' import TableRow from '@tiptap/extension-table-row' import TableHeader from '@tiptap/extension-table-header' import TableCell from '@tiptap/extension-table-cell' import Image from '@tiptap/extension-image' interface TechFileEditorProps { content: string onSave: (content: string) => void readOnly?: boolean } function normalizeContent(content: string): string { if (!content) return '

' const trimmed = content.trim() // If it looks like JSON array or has no HTML tags, wrap in

if (trimmed.startsWith('[') || !/<[a-z][\s\S]*>/i.test(trimmed)) { return `

${trimmed.replace(/\n/g, '

')}

` } return trimmed } interface ToolbarButtonProps { onClick: () => void isActive?: boolean disabled?: boolean title: string children: React.ReactNode } function ToolbarButton({ onClick, isActive, disabled, title, children }: ToolbarButtonProps) { return ( ) } export function TechFileEditor({ content, onSave, readOnly = false }: TechFileEditorProps) { const debounceTimer = useRef | null>(null) const onSaveRef = useRef(onSave) onSaveRef.current = onSave const debouncedSave = useCallback((html: string) => { if (debounceTimer.current) { clearTimeout(debounceTimer.current) } debounceTimer.current = setTimeout(() => { onSaveRef.current(html) }, 3000) }, []) const editor = useEditor({ extensions: [ StarterKit.configure({ heading: { levels: [2, 3, 4] }, }), Table.configure({ resizable: true, HTMLAttributes: { class: 'border-collapse border border-gray-300' }, }), TableRow, TableHeader, TableCell, Image.configure({ HTMLAttributes: { class: 'max-w-full rounded' }, }), ], content: normalizeContent(content), editable: !readOnly, onUpdate: ({ editor: ed }: { editor: Editor }) => { if (!readOnly) { debouncedSave(ed.getHTML()) } }, editorProps: { attributes: { class: 'prose prose-sm max-w-none dark:prose-invert focus:outline-none min-h-[300px] px-4 py-3', }, }, }) // Update content when parent prop changes useEffect(() => { if (editor && content) { const normalized = normalizeContent(content) const currentHTML = editor.getHTML() if (normalized !== currentHTML) { editor.commands.setContent(normalized) } } }, [content, editor]) // Update editable state when readOnly changes useEffect(() => { if (editor) { editor.setEditable(!readOnly) } }, [readOnly, editor]) // Cleanup debounce timer useEffect(() => { return () => { if (debounceTimer.current) { clearTimeout(debounceTimer.current) } } }, []) if (!editor) { return (
) } return (
{/* Toolbar */} {!readOnly && (
{/* Text formatting */} editor.chain().focus().toggleBold().run()} isActive={editor.isActive('bold')} title="Fett (Ctrl+B)" > editor.chain().focus().toggleItalic().run()} isActive={editor.isActive('italic')} title="Kursiv (Ctrl+I)" >
{/* Headings */} editor.chain().focus().toggleHeading({ level: 2 }).run()} isActive={editor.isActive('heading', { level: 2 })} title="Ueberschrift 2" > H2 editor.chain().focus().toggleHeading({ level: 3 }).run()} isActive={editor.isActive('heading', { level: 3 })} title="Ueberschrift 3" > H3 editor.chain().focus().toggleHeading({ level: 4 }).run()} isActive={editor.isActive('heading', { level: 4 })} title="Ueberschrift 4" > H4
{/* Lists */} editor.chain().focus().toggleBulletList().run()} isActive={editor.isActive('bulletList')} title="Aufzaehlung" > editor.chain().focus().toggleOrderedList().run()} isActive={editor.isActive('orderedList')} title="Nummerierte Liste" >
{/* Table */} editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()} title="Tabelle einfuegen (3x3)" > {/* Blockquote */} editor.chain().focus().toggleBlockquote().run()} isActive={editor.isActive('blockquote')} title="Zitat" > {/* Code Block */} editor.chain().focus().toggleCodeBlock().run()} isActive={editor.isActive('codeBlock')} title="Code-Block" >
{/* Undo / Redo */} editor.chain().focus().undo().run()} disabled={!editor.can().undo()} title="Rueckgaengig (Ctrl+Z)" > editor.chain().focus().redo().run()} disabled={!editor.can().redo()} title="Wiederholen (Ctrl+Shift+Z)" >
)} {/* Editor Content */}
) }