From aa5698fdc599203de292b428d1a397c823a7e652 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:17:31 +0330 Subject: [PATCH] Import source: vault/components/code-viewer Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- vault/components/code-viewer.tsx | 149 +++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 vault/components/code-viewer.tsx diff --git a/vault/components/code-viewer.tsx b/vault/components/code-viewer.tsx new file mode 100644 index 0000000..bec4971 --- /dev/null +++ b/vault/components/code-viewer.tsx @@ -0,0 +1,149 @@ +"use client"; + +import dynamic from "next/dynamic"; +import { useState, useEffect, useCallback } from "react"; +import { Copy, Check } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { EditorView } from "@codemirror/view"; +import type { Extension } from "@codemirror/state"; + +const CodeMirrorViewer = dynamic( + () => import("@uiw/react-codemirror").then((mod) => mod.default), + { ssr: false } +); + +const langExtensions: Record Promise> = { + javascript: () => import("@codemirror/lang-javascript").then((m) => m.javascript()), + typescript: () => import("@codemirror/lang-javascript").then((m) => m.javascript({ jsx: true, typescript: true })), + python: () => import("@codemirror/lang-python").then((m) => m.python()), + html: () => import("@codemirror/lang-html").then((m) => m.html()), + css: () => import("@codemirror/lang-css").then((m) => m.css()), + json: () => import("@codemirror/lang-json").then((m) => m.json()), + markdown: () => import("@codemirror/lang-markdown").then((m) => m.markdown()), + rust: () => import("@codemirror/lang-rust").then((m) => m.rust()), + go: () => import("@codemirror/lang-go").then((m) => m.go()), + java: () => import("@codemirror/lang-java").then((m) => m.java()), + cpp: () => import("@codemirror/lang-cpp").then((m) => m.cpp()), + xml: () => import("@codemirror/lang-xml").then((m) => m.xml()), + php: () => import("@codemirror/lang-php").then((m) => m.php()), +}; + +type ThemeName = "dark" | "light" | "dracula" | "monokai" | "github-dark" | "nord"; + +const THEMES: { value: ThemeName; label: string }[] = [ + { value: "dark", label: "One Dark" }, + { value: "dracula", label: "Dracula" }, + { value: "monokai", label: "Monokai" }, + { value: "nord", label: "Nord" }, + { value: "github-dark", label: "GitHub Dark" }, + { value: "light", label: "Light" }, +]; + +const THEME_STORAGE_KEY = "cvault:editor-theme"; + +async function loadThemeExtension(name: ThemeName): Promise<"dark" | "light" | Extension> { + switch (name) { + case "dark": return "dark"; + case "light": return "light"; + case "dracula": { + const { dracula } = await import("@uiw/codemirror-theme-dracula"); + return dracula; + } + case "monokai": { + const { monokai } = await import("@uiw/codemirror-theme-monokai"); + return monokai; + } + case "github-dark": { + const { githubDark } = await import("@uiw/codemirror-theme-github"); + return githubDark; + } + case "nord": { + const { nord } = await import("@uiw/codemirror-theme-nord"); + return nord; + } + } +} + +interface CodeViewerProps { + code: string; + language: string; +} + +export function CodeViewer({ code, language }: CodeViewerProps) { + const [langExtension, setLangExtension] = useState(null); + const [copied, setCopied] = useState(false); + const [themeName, setThemeName] = useState("dark"); + const [themeExtension, setThemeExtension] = useState<"dark" | "light" | Extension>("dark"); + + useEffect(() => { + const stored = localStorage.getItem(THEME_STORAGE_KEY) as ThemeName | null; + if (stored && THEMES.some((t) => t.value === stored)) setThemeName(stored); + }, []); + + useEffect(() => { + loadThemeExtension(themeName).then(setThemeExtension); + }, [themeName]); + + useEffect(() => { + const loader = langExtensions[language]; + if (loader) { + loader().then((ext) => setLangExtension(ext as Extension)); + } else { + setLangExtension(null); + } + }, [language]); + + const handleCopy = useCallback(async () => { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }, [code]); + + const handleThemeChange = useCallback((name: ThemeName) => { + setThemeName(name); + localStorage.setItem(THEME_STORAGE_KEY, name); + }, []); + + const extensions: Extension[] = [ + ...(langExtension ? [langExtension] : []), + EditorView.lineWrapping, + EditorView.editable.of(false), + ]; + + return ( +
+
+ + +
+ +
+ ); +}