mirror of
https://github.com/cfngc4594/monaco-editor-lsp-next.git
synced 2026-05-31 10:18:52 +00:00
feat(settings): add code editor preferences
This commit is contained in:
parent
705e0f29e4
commit
12c2963447
@ -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": {
|
||||
|
||||
@ -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": {
|
||||
|
||||
88
src/components/code-editor-settings.tsx
Normal file
88
src/components/code-editor-settings.tsx
Normal file
@ -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 (
|
||||
<div className="max-w-md space-y-6">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("fontSize")}</Label>
|
||||
<Select
|
||||
value={editorSettings.fontSize.toString()}
|
||||
onValueChange={(value) =>
|
||||
setEditorSetting("fontSize", Number(value))
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[160px] shadow-none focus:ring-0">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{fontSizes.map((fontSize) => (
|
||||
<SelectItem key={fontSize} value={fontSize.toString()}>
|
||||
{fontSize}px
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<EditorSwitch
|
||||
label={t("wordWrap")}
|
||||
description={t("wordWrapDescription")}
|
||||
checked={editorSettings.wordWrap}
|
||||
onCheckedChange={(checked) => setEditorSetting("wordWrap", checked)}
|
||||
/>
|
||||
<EditorSwitch
|
||||
label={t("minimap")}
|
||||
description={t("minimapDescription")}
|
||||
checked={editorSettings.minimap}
|
||||
onCheckedChange={(checked) => setEditorSetting("minimap", checked)}
|
||||
/>
|
||||
<EditorSwitch
|
||||
label={t("stickyScroll")}
|
||||
description={t("stickyScrollDescription")}
|
||||
checked={editorSettings.stickyScroll}
|
||||
onCheckedChange={(checked) => setEditorSetting("stickyScroll", checked)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface EditorSwitchProps {
|
||||
label: string;
|
||||
description: string;
|
||||
checked: boolean;
|
||||
onCheckedChange: (checked: boolean) => void;
|
||||
}
|
||||
|
||||
function EditorSwitch({
|
||||
label,
|
||||
description,
|
||||
checked,
|
||||
onCheckedChange,
|
||||
}: EditorSwitchProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<Label>{label}</Label>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
<Switch checked={checked} onCheckedChange={onCheckedChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -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<editor.IStandaloneDiffEditor | null>(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={<Loading />}
|
||||
className={className}
|
||||
/>
|
||||
|
||||
@ -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<editor.IStandaloneCodeEditor | null>(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={<Loading />}
|
||||
className={className}
|
||||
/>
|
||||
|
||||
@ -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 = () => {
|
||||
<div className="flex flex-col gap-4">
|
||||
{activeSetting === "Appearance" && <AppearanceSettings />}
|
||||
{activeSetting === "Language" && <LocaleSwitcher />}
|
||||
{activeSetting === "CodeEditor" && <CodeEditorSettings />}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</main>
|
||||
|
||||
@ -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: <K extends keyof EditorSettings>(
|
||||
key: K,
|
||||
value: EditorSettings[K]
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const useSettingsStore = create<SettingsState>()(
|
||||
@ -13,14 +25,28 @@ export const useSettingsStore = create<SettingsState>()(
|
||||
(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,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user