Import source: vault/components/code-editor
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,140 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const CodeMirrorEditor = dynamic(
|
||||||
|
() => import("@uiw/react-codemirror").then((mod) => mod.default),
|
||||||
|
{ ssr: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
const langExtensions: Record<string, () => Promise<unknown>> = {
|
||||||
|
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()),
|
||||||
|
};
|
||||||
|
|
||||||
|
import { useState, useEffect, useCallback } from "react";
|
||||||
|
import { EditorView } from "@codemirror/view";
|
||||||
|
import type { Extension } from "@codemirror/state";
|
||||||
|
|
||||||
|
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 CodeEditorProps {
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
language: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CodeEditor({ value, onChange, language }: CodeEditorProps) {
|
||||||
|
const [langExtension, setLangExtension] = useState<Extension | null>(null);
|
||||||
|
const [themeName, setThemeName] = useState<ThemeName>("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 handleThemeChange = useCallback((name: ThemeName) => {
|
||||||
|
setThemeName(name);
|
||||||
|
localStorage.setItem(THEME_STORAGE_KEY, name);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const extensions: Extension[] = [
|
||||||
|
...(langExtension ? [langExtension] : []),
|
||||||
|
EditorView.lineWrapping,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-lg overflow-hidden border border-border/50">
|
||||||
|
<div className="flex items-center justify-between px-3 py-1.5 bg-muted/40 border-b border-border/30">
|
||||||
|
<select
|
||||||
|
value={themeName}
|
||||||
|
onChange={(e) => handleThemeChange(e.target.value as ThemeName)}
|
||||||
|
className="text-xs bg-transparent text-muted-foreground hover:text-foreground cursor-pointer outline-none py-0.5 pr-1"
|
||||||
|
>
|
||||||
|
{THEMES.map((t) => (
|
||||||
|
<option key={t.value} value={t.value} className="bg-background text-foreground">
|
||||||
|
{t.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<span className="text-xs text-muted-foreground/50">editor</span>
|
||||||
|
</div>
|
||||||
|
{CodeMirrorEditor && (
|
||||||
|
<CodeMirrorEditor
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
extensions={extensions}
|
||||||
|
theme={themeExtension}
|
||||||
|
height="400px"
|
||||||
|
basicSetup={{
|
||||||
|
lineNumbers: true,
|
||||||
|
highlightActiveLine: true,
|
||||||
|
bracketMatching: true,
|
||||||
|
closeBrackets: true,
|
||||||
|
indentOnInput: true,
|
||||||
|
foldGutter: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user