mirror of
https://litchi.icu/ngc2207/judge.git
synced 2025-05-19 04:56:33 +00:00
feat: add theme and language selection to code editor with language server connection support
This commit is contained in:
parent
937330ac3d
commit
1aeb472495
@ -3,16 +3,33 @@ import {
|
|||||||
DEFAULT_EDITOR_THEME,
|
DEFAULT_EDITOR_THEME,
|
||||||
DEFAULT_EDITOR_LANGUAGE,
|
DEFAULT_EDITOR_LANGUAGE,
|
||||||
} from "@/config";
|
} from "@/config";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
import { Palette } from "lucide-react";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import { highlighter } from "@/lib/shiki";
|
import { highlighter } from "@/lib/shiki";
|
||||||
import { Bot, CodeXml } from "lucide-react";
|
import { Bot, CodeXml } from "lucide-react";
|
||||||
import { shikiToMonaco } from "@shikijs/monaco";
|
import { shikiToMonaco } from "@shikijs/monaco";
|
||||||
import { connectToLanguageServer } from "@/lib/lsp";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { SUPPORTED_EDITOR_THEMES } from "@/constants/themes";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||||
import { DiffEditor, Editor, Monaco, loader } from "@monaco-editor/react";
|
import { DiffEditor, Editor, Monaco, loader } from "@monaco-editor/react";
|
||||||
|
import { SUPPORTED_EDITOR_LANGUAGES_CONFIG } from "@/constants/languages";
|
||||||
|
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
|
||||||
|
import { connectToLanguageServer, SUPPORTED_LSP_LANGUAGES } from "@/lib/lsp";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
|
||||||
|
self.MonacoEnvironment = {
|
||||||
|
getWorker(_, _label) {
|
||||||
|
return new editorWorker();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
loader.config({ monaco });
|
loader.config({ monaco });
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@ -25,9 +42,12 @@ function App() {
|
|||||||
if (webSocketRef.current) {
|
if (webSocketRef.current) {
|
||||||
webSocketRef.current.close();
|
webSocketRef.current.close();
|
||||||
}
|
}
|
||||||
connectToLanguageServer(language).then((webSocket) => {
|
|
||||||
webSocketRef.current = webSocket;
|
if (language in SUPPORTED_LSP_LANGUAGES) {
|
||||||
});
|
connectToLanguageServer(language).then((webSocket) => {
|
||||||
|
webSocketRef.current = webSocket;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (webSocketRef.current) {
|
if (webSocketRef.current) {
|
||||||
@ -40,32 +60,66 @@ function App() {
|
|||||||
<div className="h-screen bg-[#282c34] dark overflow-hidden">
|
<div className="h-screen bg-[#282c34] dark overflow-hidden">
|
||||||
<Tabs defaultValue="code-editor" className="h-full mt-2">
|
<Tabs defaultValue="code-editor" className="h-full mt-2">
|
||||||
<ScrollArea className="border-b border-[#3e4452]">
|
<ScrollArea className="border-b border-[#3e4452]">
|
||||||
<TabsList className="gap-1 bg-transparent px-4 mb-2">
|
<div className="flex items-center justify-between">
|
||||||
<TabsTrigger
|
<TabsList className="gap-1 bg-transparent px-4 mb-2">
|
||||||
value="code-editor"
|
<TabsTrigger
|
||||||
className="rounded-full data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-none"
|
value="code-editor"
|
||||||
>
|
className="rounded-full data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-none"
|
||||||
<CodeXml
|
>
|
||||||
className="-ms-0.5 me-1.5 opacity-60"
|
<CodeXml
|
||||||
size={16}
|
className="-ms-0.5 me-1.5 opacity-60"
|
||||||
strokeWidth={2}
|
size={16}
|
||||||
aria-hidden="true"
|
strokeWidth={2}
|
||||||
/>
|
aria-hidden="true"
|
||||||
代码
|
/>
|
||||||
</TabsTrigger>
|
代码
|
||||||
<TabsTrigger
|
</TabsTrigger>
|
||||||
value="diff-editor"
|
<TabsTrigger
|
||||||
className="500 rounded-full data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-none"
|
value="diff-editor"
|
||||||
>
|
className="500 rounded-full data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=active]:shadow-none"
|
||||||
<Bot
|
>
|
||||||
className="-ms-0.5 me-1.5 opacity-60"
|
<Bot
|
||||||
size={16}
|
className="-ms-0.5 me-1.5 opacity-60"
|
||||||
strokeWidth={2}
|
size={16}
|
||||||
aria-hidden="true"
|
strokeWidth={2}
|
||||||
/>
|
aria-hidden="true"
|
||||||
AI 助教
|
/>
|
||||||
</TabsTrigger>
|
AI 助教
|
||||||
</TabsList>
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<div className="flex items-center gap-x-2 pr-4 mb-2">
|
||||||
|
<Select value={theme} onValueChange={setTheme}>
|
||||||
|
<SelectTrigger className="relative ps-9 w-40 bg-foreground">
|
||||||
|
<div className="pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center ps-3 text-muted-foreground/80 group-has-[[disabled]]:opacity-50">
|
||||||
|
<Palette size={16} strokeWidth={2} aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
<SelectValue placeholder="Select Theme" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{SUPPORTED_EDITOR_THEMES.map((theme) => (
|
||||||
|
<SelectItem key={theme.id} value={theme.id}>
|
||||||
|
<span className="truncate">{theme.label}</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Select value={language} onValueChange={setLanguage}>
|
||||||
|
<SelectTrigger className="[&>span]:flex [&>span]:items-center [&>span]:gap-2 [&>span_svg]:shrink-0 [&>span_svg]:text-muted-foreground/80 w-36 bg-foreground">
|
||||||
|
<SelectValue placeholder="Select Language" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="[&_*[role=option]>span>svg]:shrink-0 [&_*[role=option]>span>svg]:text-muted-foreground/80 [&_*[role=option]>span]:end-2 [&_*[role=option]>span]:start-auto [&_*[role=option]>span]:flex [&_*[role=option]>span]:items-center [&_*[role=option]>span]:gap-2 [&_*[role=option]]:pe-8 [&_*[role=option]]:ps-2">
|
||||||
|
{Object.values(SUPPORTED_EDITOR_LANGUAGES_CONFIG).map(
|
||||||
|
(language) => (
|
||||||
|
<SelectItem key={language.id} value={language.id}>
|
||||||
|
<language.icon size={16} aria-hidden="true" />
|
||||||
|
<span className="truncate">{language.label}</span>
|
||||||
|
</SelectItem>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ScrollBar orientation="horizontal" />
|
<ScrollBar orientation="horizontal" />
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
<TabsContent value="code-editor" className="h-full mt-0">
|
<TabsContent value="code-editor" className="h-full mt-0">
|
||||||
@ -98,9 +152,11 @@ function App() {
|
|||||||
"*"
|
"*"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
connectToLanguageServer(language).then((webSocket) => {
|
if (language in SUPPORTED_LSP_LANGUAGES) {
|
||||||
webSocketRef.current = webSocket;
|
connectToLanguageServer(language).then((webSocket) => {
|
||||||
});
|
webSocketRef.current = webSocket;
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
|
@ -28,15 +28,6 @@ using namespace std;
|
|||||||
int main() {
|
int main() {
|
||||||
cout << "Hello, World!";
|
cout << "Hello, World!";
|
||||||
return 0;
|
return 0;
|
||||||
}`,
|
|
||||||
},
|
|
||||||
java: {
|
|
||||||
path: "playground/Main.java",
|
|
||||||
language: "java",
|
|
||||||
value: `public class Main {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println("Hello, World!");
|
|
||||||
}
|
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { COriginal, CplusplusOriginal, JavaOriginal } from "devicons-react";
|
import { COriginal, CplusplusOriginal } from "devicons-react";
|
||||||
|
|
||||||
export const SUPPORTED_EDITOR_LANGUAGES = ["c", "cpp", "java"];
|
export const SUPPORTED_EDITOR_LANGUAGES = ["c", "cpp"];
|
||||||
|
|
||||||
export const SUPPORTED_EDITOR_LANGUAGES_CONFIG = {
|
export const SUPPORTED_EDITOR_LANGUAGES_CONFIG = {
|
||||||
c: {
|
c: {
|
||||||
@ -13,11 +13,6 @@ export const SUPPORTED_EDITOR_LANGUAGES_CONFIG = {
|
|||||||
label: "C++",
|
label: "C++",
|
||||||
icon: CplusplusOriginal,
|
icon: CplusplusOriginal,
|
||||||
},
|
},
|
||||||
java: {
|
|
||||||
id: "java",
|
|
||||||
label: "Java",
|
|
||||||
icon: JavaOriginal,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SupportedEditorLanguage =
|
export type SupportedEditorLanguage =
|
||||||
|
Loading…
Reference in New Issue
Block a user