import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { Image } from '@tiptap/extension-image'; import { TaskItem, TaskList } from '@tiptap/extension-list'; import { TableKit } from '@tiptap/extension-table'; import { type Editor, EditorContent, useEditor, useEditorState, } from '@tiptap/react'; // eslint-disable-next-line import/no-named-as-default import StarterKit from '@tiptap/starter-kit'; import { LuBold, LuChevronDown, LuCode, LuFileCode, LuImage, LuItalic, LuLink, LuList, LuListOrdered, LuListTodo, LuMinus, LuQuote, LuRedo2, LuStrikethrough, LuTable, LuUndo2, } from 'react-icons/lu'; import { cn } from '../copilot/cn'; import { htmlToMarkdown, markdownToEditorHtml } from './markdown-editor.shared'; interface MarkdownEditorProps { value: string; onChange: (markdown: string) => void; disabled?: boolean; className?: string; } const BLOCK_OPTIONS = [ { value: 'p', label: 'Paragraph' }, { value: 'h1', label: 'Heading 1' }, { value: 'h2', label: 'Heading 2' }, { value: 'h3', label: 'Heading 3' }, { value: 'h4', label: 'Heading 4' }, ] as const; type BlockValue = (typeof BLOCK_OPTIONS)[number]['value']; function ToolButton({ onClick, active = false, disabled = false, label, children, }: { onClick: () => void; active?: boolean; disabled?: boolean; label: string; children: React.ReactNode; }): JSX.Element { return ( ); } function ToolDivider(): JSX.Element { return ( ); } const activeBlock = (editor: Editor): BlockValue => { for (const level of [1, 2, 3, 4] as const) { if (editor.isActive('heading', { level })) return `h${level}` as BlockValue; } return 'p'; }; function useToolbarState(editor: Editor) { return useEditorState({ editor, selector: ({ editor: instance }) => ({ canUndo: instance.can().undo(), canRedo: instance.can().redo(), block: activeBlock(instance), bold: instance.isActive('bold'), italic: instance.isActive('italic'), strike: instance.isActive('strike'), code: instance.isActive('code'), link: instance.isActive('link'), bulletList: instance.isActive('bulletList'), orderedList: instance.isActive('orderedList'), taskList: instance.isActive('taskList'), blockquote: instance.isActive('blockquote'), codeBlock: instance.isActive('codeBlock'), table: instance.isActive('table'), }), }); } function Toolbar({ editor }: { editor: Editor }): JSX.Element { const state = useToolbarState(editor); const setBlock = (value: BlockValue): void => { if (value === 'p') { editor.chain().focus().setParagraph().run(); return; } const level = Number(value.slice(1)) as 1 | 2 | 3 | 4; editor.chain().focus().setHeading({ level }).run(); }; const promptForLink = (): void => { const previous = editor.getAttributes('link').href as string | undefined; const href = window.prompt('Link URL', previous ?? 'https://'); if (href === null) return; if (href.trim() === '') { editor.chain().focus().extendMarkRange('link').unsetLink().run(); return; } editor .chain() .focus() .extendMarkRange('link') .setLink({ href: href.trim() }) .run(); }; const promptForImage = (): void => { const src = window.prompt('Image URL', 'https://'); if (!src || !src.trim()) return; const alt = window.prompt('Alt text (describes the image)', '') ?? ''; editor.chain().focus().setImage({ src: src.trim(), alt }).run(); }; return (