diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index d50795c..5e42c90 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,39 +1,68 @@
+"use client"
+
import "@/app/globals.css";
-import { Toaster } from "sonner";
-import type { Metadata } from "next";
-import { getLocale } from "next-intl/server";
-import { NextIntlClientProvider } from "next-intl";
-import { ThemeProvider } from "@/components/theme-provider";
-import { SettingsDialog } from "@/components/settings-dialog";
+import { Inter } from 'next/font/google';
+import { ThemeProvider } from '@/components/theme-provider';
+import { ThemeToggle } from '@/components/theme-toggle';
+import { EditorConfigPanel } from '@/components/editor-config-panel';
+import {useState} from "react";
-export const metadata: Metadata = {
- title: "Judge4c",
- description:
- "A full-stack, open-source online judge platform designed to elevate college programming education.",
-};
+const inter = Inter({
+ subsets: ['latin'],
+ display: 'swap',
+});
-interface RootLayoutProps {
+export default function RootLayout({
+ children,
+}: {
children: React.ReactNode;
-}
-
-export default async function RootLayout({ children }: RootLayoutProps) {
- const locale = await getLocale();
-
+}) {
+ const [showConfigPanel, setShowConfigPanel] = useState(false);
+
return (
-
-
-
-
- {children}
-
-
-
-
+
+
+ Judge4C
+
+
+
+
+
+ {/* 新增的配置面板按钮 */}
+
+
+
+
+
+ {/* 配置面板 */}
+ {showConfigPanel && (
+
+
+
+
+
+
+
+
+ )}
+
+ {children}
+
);
diff --git a/src/components/editor-config-panel.tsx b/src/components/editor-config-panel.tsx
new file mode 100644
index 0000000..2b7a535
--- /dev/null
+++ b/src/components/editor-config-panel.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { useEditorConfigStore } from '@/lib/store';
+
+export const EditorConfigPanel = () => {
+ const { config, updateConfig } = useEditorConfigStore();
+
+ const handleFontFamilyChange = (e: React.ChangeEvent) => {
+ updateConfig({ fontFamily: e.target.value });
+ };
+
+ const handleFontSizeChange = (e: React.ChangeEvent) => {
+ updateConfig({ fontSize: parseInt(e.target.value) });
+ };
+
+ const handleLineHeightChange = (e: React.ChangeEvent) => {
+ updateConfig({ lineHeight: parseInt(e.target.value) });
+ };
+
+ const handleReset = () => {
+ useEditorConfigStore.getState().resetConfig();
+ };
+
+ return (
+
+
编辑器配置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/components/problem-editor.tsx b/src/components/problem-editor.tsx
index e0f8d8e..6b89159 100644
--- a/src/components/problem-editor.tsx
+++ b/src/components/problem-editor.tsx
@@ -11,51 +11,84 @@ import { useCallback, useEffect, useRef } from "react";
import { connectToLanguageServer } from "@/lib/language-server";
import type { MonacoLanguageClient } from "monaco-languageclient";
import { DefaultEditorOptionConfig } from "@/config/editor-option";
+import { useEditorConfigStore } from '@/lib/store'; // 新增导入
+import * as monaco from 'monaco-editor';
-// Dynamically import Monaco Editor with SSR disabled
-const Editor = dynamic(
- async () => {
- await import("vscode");
- const monaco = await import("monaco-editor");
+export const ProblemEditor = () => {
+ const editorRef = useRef(null);
+ const monacoRef = useRef(null);
+
+ // 使用配置状态
+ const { config } = useEditorConfigStore();
- self.MonacoEnvironment = {
- getWorker(_, label) {
- if (label === "json") {
- return new Worker(
- new URL("monaco-editor/esm/vs/language/json/json.worker.js", import.meta.url)
- );
- }
- if (label === "css" || label === "scss" || label === "less") {
- return new Worker(
- new URL("monaco-editor/esm/vs/language/css/css.worker.js", import.meta.url)
- );
- }
- if (label === "html" || label === "handlebars" || label === "razor") {
- return new Worker(
- new URL("monaco-editor/esm/vs/language/html/html.worker.js", import.meta.url)
- );
- }
- if (label === "typescript" || label === "javascript") {
- return new Worker(
- new URL("monaco-editor/esm/vs/language/typescript/ts.worker.js", import.meta.url)
- );
- }
- return new Worker(
- new URL("monaco-editor/esm/vs/editor/editor.worker.js", import.meta.url)
- );
- },
- };
- const { loader } = await import("@monaco-editor/react");
- loader.config({ monaco });
- return (await import("@monaco-editor/react")).Editor;
- },
- {
- ssr: false,
- loading: () => ,
- }
-);
+ useEffect(() => {
+ if (!monacoRef.current || !editorRef.current) return;
+
+ // 设置语言和主题
+ const { languages } = monaco;
+ monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
+ // target保持使用ScriptTarget
+ target: monaco.languages.typescript.ScriptTarget.ESNext,
+ // module使用正确的ModuleKind类型
+ module: monaco.languages.typescript.ModuleKind.ESNext,
+ strict: true,
+ jsx: monaco.languages.typescript.JsxEmit.React,
+ esModuleInterop: true,
+ isolatedModules: true,
+ experimentalDecorators: true,
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ allowJs: true,
+ allowSyntheticDefaultImports: true,
+ typeRoots: ["../node_modules/@types"],
+ });
+
+ // 应用用户配置
+ editorRef.current.updateOptions(config);
+ }, []);
+
+ // Dynamically import Monaco Editor with SSR disabled
+ const Editor = dynamic(
+ async () => {
+ await import("vscode");
+ const monaco = await import("monaco-editor");
+
+ self.MonacoEnvironment = {
+ getWorker(_, label) {
+ if (label === "json") {
+ return new Worker(
+ new URL("monaco-editor/esm/vs/language/json/json.worker.js", import.meta.url)
+ );
+ }
+ if (label === "css" || label === "scss" || label === "less") {
+ return new Worker(
+ new URL("monaco-editor/esm/vs/language/css/css.worker.js", import.meta.url)
+ );
+ }
+ if (label === "html" || label === "handlebars" || label === "razor") {
+ return new Worker(
+ new URL("monaco-editor/esm/vs/language/html/html.worker.js", import.meta.url)
+ );
+ }
+ if (label === "typescript" || label === "javascript") {
+ return new Worker(
+ new URL("monaco-editor/esm/vs/language/typescript/ts.worker.js", import.meta.url)
+ );
+ }
+ return new Worker(
+ new URL("monaco-editor/esm/vs/editor/editor.worker.js", import.meta.url)
+ );
+ },
+ };
+ const { loader } = await import("@monaco-editor/react");
+ loader.config({ monaco });
+ return (await import("@monaco-editor/react")).Editor;
+ },
+ {
+ ssr: false,
+ loading: () => ,
+ }
+ );
-export function ProblemEditor() {
const {
hydrated,
editor,
diff --git a/src/lib/store.ts b/src/lib/store.ts
new file mode 100644
index 0000000..f090145
--- /dev/null
+++ b/src/lib/store.ts
@@ -0,0 +1,37 @@
+import { create } from 'zustand';
+import { editor } from 'monaco-editor';
+import { DefaultEditorOptionConfig } from '@/config/editor-option';
+
+interface EditorConfigState {
+ config: editor.IEditorConstructionOptions;
+ updateConfig: (newConfig: Partial) => void;
+ resetConfig: () => void;
+ defaultConfig: editor.IEditorConstructionOptions;
+}
+
+export const useEditorConfigStore = create((set) => {
+ // 从localStorage读取保存的配置
+ const savedConfig = localStorage.getItem('editorConfig');
+ const parsedConfig = savedConfig ? JSON.parse(savedConfig) : {};
+
+ return {
+ config: {
+ ...DefaultEditorOptionConfig,
+ ...parsedConfig,
+ },
+ defaultConfig: DefaultEditorOptionConfig,
+ updateConfig: (newConfig) => set((state) => {
+ const updatedConfig = {
+ ...state.config,
+ ...newConfig,
+ };
+ // 保存到localStorage
+ localStorage.setItem('editorConfig', JSON.stringify(updatedConfig));
+ return { config: updatedConfig };
+ }),
+ resetConfig: () => set((state) => {
+ localStorage.removeItem('editorConfig');
+ return { config: state.defaultConfig };
+ }),
+ };
+});
\ No newline at end of file