"use client"; import { EditorCommand, EditorCommandEmpty, EditorCommandItem, EditorCommandList, EditorContent, type EditorInstance, EditorRoot, ImageResizer, type JSONContent, handleCommandNavigation, handleImageDrop, handleImagePaste, } from "novel"; 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]; const ReportEditor = () => { const [initialContent, setInitialContent] = useState( null, ); const [saveStatus, setSaveStatus] = useState("Saved"); const [charsCount, setCharsCount] = useState(); 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) => { const json = editor.getJSON(); setCharsCount(editor.storage.characterCount.words()); window.localStorage.setItem( "html-content", highlightCodeblocks(editor.getHTML()), ); window.localStorage.setItem("novel-content", JSON.stringify(json)); window.localStorage.setItem( "markdown", editor.storage.markdown.getMarkdown(), ); setSaveStatus("Saved"); }, 500, ); useEffect(() => { const content = window.localStorage.getItem("novel-content"); if (content) setInitialContent(JSON.parse(content)); else setInitialContent(defaultEditorContent); }, []); if (!initialContent) return null; return (
{saveStatus}
{charsCount} Words
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;