diff --git a/messages/en.json b/messages/en.json
index a27829d..13523fe 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -75,6 +75,15 @@
"showPassword": "Show password",
"hidePassword": "Hide password"
},
+ "CodeEditorSettings": {
+ "fontSize": "Font size",
+ "wordWrap": "Wrap long lines",
+ "wordWrapDescription": "Move code to the next line when it does not fit in one line.",
+ "minimap": "Minimap",
+ "minimapDescription": "Show a code overview on the right side of the editor.",
+ "stickyScroll": "Sticky scroll",
+ "stickyScrollDescription": "Keep the current code scope visible while scrolling."
+ },
"DetailsPage": {
"BackButton": "All Submissions",
"Time": "Submitted on",
@@ -129,8 +138,7 @@
"nav": {
"Appearance": "Appearance",
"Language": "Language",
- "CodeEditor": "CodeEditor",
- "Advanced": "Advanced"
+ "CodeEditor": "CodeEditor"
}
},
"SignInForm": {
diff --git a/messages/zh.json b/messages/zh.json
index 79d966f..a7a02e4 100644
--- a/messages/zh.json
+++ b/messages/zh.json
@@ -75,6 +75,15 @@
"showPassword": "显示密码",
"hidePassword": "隐藏密码"
},
+ "CodeEditorSettings": {
+ "fontSize": "字体大小",
+ "wordWrap": "超长行换行显示",
+ "wordWrapDescription": "一行代码显示不下时,自动换到下一行显示。",
+ "minimap": "小地图",
+ "minimapDescription": "在编辑器右侧显示代码缩略导航。",
+ "stickyScroll": "粘性滚动",
+ "stickyScrollDescription": "滚动时固定显示当前所在的代码结构。"
+ },
"DetailsPage": {
"BackButton": "所有提交记录",
"Time": "提交于",
@@ -129,8 +138,7 @@
"nav": {
"Appearance": "外观",
"Language": "语言",
- "CodeEditor": "代码编辑器",
- "Advanced": "高级设置"
+ "CodeEditor": "代码编辑器"
}
},
"SignInForm": {
diff --git a/src/components/code-editor-settings.tsx b/src/components/code-editor-settings.tsx
new file mode 100644
index 0000000..b51c5fd
--- /dev/null
+++ b/src/components/code-editor-settings.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+import { useTranslations } from "next-intl";
+import { Label } from "@/components/ui/label";
+import { Switch } from "@/components/ui/switch";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { useSettingsStore } from "@/stores/useSettingsStore";
+
+const fontSizes = [12, 13, 14, 15, 16, 18, 20];
+
+export function CodeEditorSettings() {
+ const t = useTranslations("CodeEditorSettings");
+ const { editorSettings, setEditorSetting } = useSettingsStore();
+
+ return (
+
+
+
+
+
+
+
setEditorSetting("wordWrap", checked)}
+ />
+ setEditorSetting("minimap", checked)}
+ />
+ setEditorSetting("stickyScroll", checked)}
+ />
+
+ );
+}
+
+interface EditorSwitchProps {
+ label: string;
+ description: string;
+ checked: boolean;
+ onCheckedChange: (checked: boolean) => void;
+}
+
+function EditorSwitch({
+ label,
+ description,
+ checked,
+ onCheckedChange,
+}: EditorSwitchProps) {
+ return (
+
+
+
+
{description}
+
+
+
+ );
+}
diff --git a/src/components/core-diff-editor.tsx b/src/components/core-diff-editor.tsx
index 9af4f9d..5ea680f 100644
--- a/src/components/core-diff-editor.tsx
+++ b/src/components/core-diff-editor.tsx
@@ -9,6 +9,7 @@ import { shikiToMonaco } from "@shikijs/monaco";
import type { Monaco } from "@monaco-editor/react";
import { DEFAULT_EDITOR_OPTIONS } from "@/config/editor";
import { useMonacoTheme } from "@/hooks/use-monaco-theme";
+import { useSettingsStore } from "@/stores/useSettingsStore";
const MonacoEditor = dynamic(
async () => {
@@ -38,6 +39,7 @@ export const CoreDiffEditor = ({
className,
}: CoreDiffEditorProps) => {
const { theme } = useMonacoTheme();
+ const { editorSettings } = useSettingsStore();
const editorRef = useRef(null);
@@ -62,7 +64,21 @@ export const CoreDiffEditor = ({
modified={modified}
beforeMount={handleBeforeMount}
onMount={handleOnMount}
- options={{ ...DEFAULT_EDITOR_OPTIONS, readOnly: true }}
+ options={{
+ ...DEFAULT_EDITOR_OPTIONS,
+ readOnly: true,
+ fontSize: editorSettings.fontSize,
+ lineHeight: Math.round(editorSettings.fontSize * 1.5),
+ wordWrap: editorSettings.wordWrap ? "on" : "off",
+ minimap: {
+ ...DEFAULT_EDITOR_OPTIONS.minimap,
+ enabled: editorSettings.minimap,
+ },
+ stickyScroll: {
+ ...DEFAULT_EDITOR_OPTIONS.stickyScroll,
+ enabled: editorSettings.stickyScroll,
+ },
+ }}
loading={}
className={className}
/>
diff --git a/src/components/core-editor.tsx b/src/components/core-editor.tsx
index 9301a7e..1c3132e 100644
--- a/src/components/core-editor.tsx
+++ b/src/components/core-editor.tsx
@@ -15,6 +15,7 @@ import type { Monaco } from "@monaco-editor/react";
import { DEFAULT_EDITOR_OPTIONS } from "@/config/editor";
import { useMonacoTheme } from "@/hooks/use-monaco-theme";
import { LanguageServerConfig } from "@/generated/client";
+import { useSettingsStore } from "@/stores/useSettingsStore";
import type { MessageTransports } from "vscode-languageclient";
import { useCallback, useEffect, useRef, useState } from "react";
import type { MonacoLanguageClient } from "monaco-languageclient";
@@ -72,6 +73,7 @@ export const CoreEditor = ({
className,
}: CoreEditorProps) => {
const { theme } = useMonacoTheme();
+ const { editorSettings } = useSettingsStore();
const [isEditorMounted, setIsEditorMounted] = useState(false);
const editorRef = useRef(null);
@@ -159,7 +161,20 @@ export const CoreEditor = ({
onMount={handleOnMount}
onChange={handleOnChange}
onValidate={handleOnValidate}
- options={DEFAULT_EDITOR_OPTIONS}
+ options={{
+ ...DEFAULT_EDITOR_OPTIONS,
+ fontSize: editorSettings.fontSize,
+ lineHeight: Math.round(editorSettings.fontSize * 1.5),
+ wordWrap: editorSettings.wordWrap ? "on" : "off",
+ minimap: {
+ ...DEFAULT_EDITOR_OPTIONS.minimap,
+ enabled: editorSettings.minimap,
+ },
+ stickyScroll: {
+ ...DEFAULT_EDITOR_OPTIONS.stickyScroll,
+ enabled: editorSettings.stickyScroll,
+ },
+ }}
loading={}
className={className}
/>
diff --git a/src/components/settings-dialog.tsx b/src/components/settings-dialog.tsx
index ac462b8..f0f1634 100644
--- a/src/components/settings-dialog.tsx
+++ b/src/components/settings-dialog.tsx
@@ -30,7 +30,8 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { useSettingsStore } from "@/stores/useSettingsStore";
import { LocaleSwitcher } from "@/components/locale-switcher";
import AppearanceSettings from "@/components/appearance-settings";
-import { CodeXml, Globe, Paintbrush, Settings } from "lucide-react";
+import { CodeEditorSettings } from "@/components/code-editor-settings";
+import { CodeXml, Globe, Paintbrush } from "lucide-react";
export const SettingsDialog = () => {
const t = useTranslations("SettingsDialog");
@@ -39,7 +40,6 @@ export const SettingsDialog = () => {
{ id: "Appearance", name: t("nav.Appearance"), icon: Paintbrush },
{ id: "Language", name: t("nav.Language"), icon: Globe },
{ id: "CodeEditor", name: t("nav.CodeEditor"), icon: CodeXml },
- { id: "Advanced", name: t("nav.Advanced"), icon: Settings },
],
};
const { isDialogOpen, activeSetting, setDialogOpen, setActiveSetting } =
@@ -99,6 +99,7 @@ export const SettingsDialog = () => {
{activeSetting === "Appearance" &&
}
{activeSetting === "Language" &&
}
+ {activeSetting === "CodeEditor" &&
}
diff --git a/src/stores/useSettingsStore.ts b/src/stores/useSettingsStore.ts
index dd66d58..f5e314d 100644
--- a/src/stores/useSettingsStore.ts
+++ b/src/stores/useSettingsStore.ts
@@ -1,11 +1,23 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
+export interface EditorSettings {
+ fontSize: number;
+ wordWrap: boolean;
+ minimap: boolean;
+ stickyScroll: boolean;
+}
+
interface SettingsState {
activeSetting: string;
isDialogOpen: boolean;
+ editorSettings: EditorSettings;
setActiveSetting: (setting: string) => void;
setDialogOpen: (open: boolean) => void;
+ setEditorSetting: (
+ key: K,
+ value: EditorSettings[K]
+ ) => void;
}
export const useSettingsStore = create()(
@@ -13,14 +25,28 @@ export const useSettingsStore = create()(
(set) => ({
activeSetting: "Appearance",
isDialogOpen: false,
+ editorSettings: {
+ fontSize: 14,
+ wordWrap: true,
+ minimap: false,
+ stickyScroll: true,
+ },
setActiveSetting: (setting) => set({ activeSetting: setting }),
setDialogOpen: (open) => set({ isDialogOpen: open }),
+ setEditorSetting: (key, value) =>
+ set((state) => ({
+ editorSettings: {
+ ...state.editorSettings,
+ [key]: value,
+ },
+ })),
}),
{
name: "settings-state",
partialize: (state) => ({
- activeNav: state.activeSetting,
+ activeSetting: state.activeSetting,
isDialogOpen: state.isDialogOpen,
+ editorSettings: state.editorSettings,
}),
}
)