// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // SPDX-License-Identifier: MIT "use client"; import { EditorCommand, EditorCommandEmpty, EditorCommandItem, EditorCommandList, EditorContent, type EditorInstance, EditorRoot, ImageResizer, type JSONContent, handleCommandNavigation, handleImageDrop, handleImagePaste, } from "novel"; import type { Content } from "@tiptap/react"; import { useEffect, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; import { defaultExtensions } from "./extensions"; import { ColorSelector } from "./selectors/color-selector"; import { LinkSelector } from "./selectors/link-selector"; import { MathSelector } from "./selectors/math-selector"; import { NodeSelector } from "./selectors/node-selector"; import { Separator } from "../ui/separator"; import GenerativeMenuSwitch from "./generative/generative-menu-switch"; import { uploadFn } from "./image-upload"; import { TextButtons } from "./selectors/text-buttons"; import { slashCommand, suggestionItems } from "./slash-command"; // import { defaultEditorContent } from "./content"; import "~/styles/prosemirror.css"; const hljs = require("highlight.js"); const extensions = [...defaultExtensions, slashCommand]; export interface ReportEditorProps { content: Content; onMarkdownChange?: (markdown: string) => void; } const ReportEditor = ({ content, onMarkdownChange }: ReportEditorProps) => { const [initialContent, setInitialContent] = useState(() => content); const [saveStatus, setSaveStatus] = useState("Saved"); const [openNode, setOpenNode] = useState(false); const [openColor, setOpenColor] = useState(false); const [openLink, setOpenLink] = useState(false); const [openAI, setOpenAI] = useState(false); //Apply Codeblock Highlighting on the HTML from editor.getHTML() const highlightCodeblocks = (content: string) => { const doc = new DOMParser().parseFromString(content, "text/html"); doc.querySelectorAll("pre code").forEach((el) => { // @ts-ignore // https://highlightjs.readthedocs.io/en/latest/api.html?highlight=highlightElement#highlightelement hljs.highlightElement(el); }); return new XMLSerializer().serializeToString(doc); }; const debouncedUpdates = useDebouncedCallback( async (editor: EditorInstance) => { if (onMarkdownChange) { const markdown = editor.storage.markdown.getMarkdown(); onMarkdownChange(markdown); } setSaveStatus("Saved"); }, 500, ); if (!initialContent) return null; return (
handleCommandNavigation(event), }, handlePaste: (view, event) => handleImagePaste(view, event, uploadFn), handleDrop: (view, event, _slice, moved) => handleImageDrop(view, event, moved, uploadFn), attributes: { class: "prose prose-base prose-p:my-4 dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full", }, }} onUpdate={({ editor }) => { debouncedUpdates(editor); setSaveStatus("Unsaved"); }} slotAfter={} > No results {suggestionItems.map((item) => ( item.command?.(val)} className="hover:bg-accent aria-selected:bg-accent flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm" key={item.title} >
{item.icon}

{item.title}

{item.description}

))}
); }; export default ReportEditor;