From 2c165d489eff7a6f7d094f75ba4d53dce22a5c3d Mon Sep 17 00:00:00 2001 From: Pranshu Patel Date: Mon, 4 Nov 2024 17:23:47 +0530 Subject: [PATCH] feat: Add theme selector to advanced editor component --- .../components/tailwind/advanced-editor.tsx | 37 +++++---- .../tailwind/selectors/theme-selector.tsx | 79 +++++++++++++++++++ apps/web/styles/globals.css | 71 +---------------- 3 files changed, 104 insertions(+), 83 deletions(-) create mode 100644 apps/web/components/tailwind/selectors/theme-selector.tsx diff --git a/apps/web/components/tailwind/advanced-editor.tsx b/apps/web/components/tailwind/advanced-editor.tsx index 526681f21..495bd7ad7 100644 --- a/apps/web/components/tailwind/advanced-editor.tsx +++ b/apps/web/components/tailwind/advanced-editor.tsx @@ -18,6 +18,7 @@ import { ColorSelector } from "./selectors/color-selector"; import { LinkSelector } from "./selectors/link-selector"; import { NodeSelector } from "./selectors/node-selector"; import { MathSelector } from "./selectors/math-selector"; +import { ThemeSelector } from "./selectors/theme-selector"; import { Separator } from "./ui/separator"; import { handleImageDrop, handleImagePaste } from "novel/plugins"; @@ -26,7 +27,7 @@ import { uploadFn } from "./image-upload"; import { TextButtons } from "./selectors/text-buttons"; import { slashCommand, suggestionItems } from "./slash-command"; -const hljs = require('highlight.js'); +import hljs from 'highlight.js'; // Ensure Highlight.js is imported const extensions = [...defaultExtensions, slashCommand]; @@ -38,23 +39,12 @@ const TailwindAdvancedEditor = () => { const [openNode, setOpenNode] = useState(false); const [openColor, setOpenColor] = useState(false); const [openLink, setOpenLink] = useState(false); + const [openTheme, setOpenTheme] = 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"); @@ -66,6 +56,24 @@ const TailwindAdvancedEditor = () => { else setInitialContent(defaultEditorContent); }, []); + useEffect(() => { + // Load the default theme when the component mounts + loadTheme('default'); + }, []); + + const loadTheme = (theme: string) => { + const existingStyle = document.getElementById('highlight-js-style'); + if (existingStyle) { + existingStyle.remove(); + } + + const style = document.createElement('link'); + style.id = 'highlight-js-style'; + style.rel = 'stylesheet'; + style.href = `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/${theme}.min.css`; + document.head.appendChild(style); + }; + if (!initialContent) return null; return ( @@ -124,7 +132,6 @@ const TailwindAdvancedEditor = () => { - @@ -132,6 +139,8 @@ const TailwindAdvancedEditor = () => { + + diff --git a/apps/web/components/tailwind/selectors/theme-selector.tsx b/apps/web/components/tailwind/selectors/theme-selector.tsx new file mode 100644 index 000000000..d77cb3233 --- /dev/null +++ b/apps/web/components/tailwind/selectors/theme-selector.tsx @@ -0,0 +1,79 @@ +import { Check, ChevronDown } from "lucide-react"; +import { EditorBubbleItem, useEditor } from "novel"; +import { Button } from "@/components/tailwind/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/tailwind/ui/popover"; +import hljs from "highlight.js"; + +export interface BubbleColorMenuItem { + name: string; +} + +const THEMES: BubbleColorMenuItem[] = [ + { name: "default" }, + { name: "github-dark" }, + { name: "github" }, + { name: "monokai" }, + { name: "docco" }, +]; + +interface ThemeSelectorProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export const ThemeSelector = ({ open, onOpenChange }: ThemeSelectorProps) => { + const { editor } = useEditor(); + + if (!editor) return null; + + const loadTheme = (theme: string) => { + const existingStyle = document.getElementById('highlight-js-style'); + if (existingStyle) { + existingStyle.remove(); + } + + const style = document.createElement('link'); + style.id = 'highlight-js-style'; + style.rel = 'stylesheet'; + style.href = `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/${theme}.min.css`; + document.head.appendChild(style); + + // Re-highlight all code blocks in the editor + const codeBlocks = document.querySelectorAll('pre code'); + codeBlocks.forEach((block) => { + block.classList.add('hljs'); + hljs.highlightElement(block as HTMLElement); // Re-highlight the code block + }); + }; + + return ( + + + + + + + {THEMES.map(({ name }) => ( + { + loadTheme(name); + onOpenChange(false); + }} + className="flex cursor-pointer items-center justify-between px-2 py-1 text-sm hover:bg-accent" + > + {name} + {editor.isActive("theme", { name }) && } + + ))} + + + ); +}; diff --git a/apps/web/styles/globals.css b/apps/web/styles/globals.css index 637377cda..61b4f087b 100644 --- a/apps/web/styles/globals.css +++ b/apps/web/styles/globals.css @@ -90,75 +90,8 @@ * { @apply border-border; } + body { @apply bg-background text-foreground; } -} - - -pre { - background: #0d0d0d; - border-radius: 0.5rem; - color: #fff; - font-family: "JetBrainsMono", monospace; - padding: 0.75rem 1rem; - - code { - background: none; - color: inherit; - font-size: 0.8rem; - padding: 0; - } - - .hljs-comment, - .hljs-quote { - color: #616161; - } - - .hljs-variable, - .hljs-template-variable, - .hljs-attribute, - .hljs-tag, - .hljs-name, - .hljs-regexp, - .hljs-link, - .hljs-name, - .hljs-selector-id, - .hljs-selector-class { - color: #f98181; - } - - .hljs-number, - .hljs-meta, - .hljs-built_in, - .hljs-builtin-name, - .hljs-literal, - .hljs-type, - .hljs-params { - color: #fbbc88; - } - - .hljs-string, - .hljs-symbol, - .hljs-bullet { - color: #b9f18d; - } - - .hljs-title, - .hljs-section { - color: #faf594; - } - - .hljs-keyword, - .hljs-selector-tag { - color: #70cff8; - } - - .hljs-emphasis { - font-style: italic; - } - - .hljs-strong { - font-weight: 700; - } -} +} \ No newline at end of file