mirror of
https://github.com/massbug/judge4c.git
synced 2025-07-04 07:40:51 +00:00
refactor(components): 重构 AI优化编辑器
- 将 AIProblemEditor 组件改为 AIEditorWrapper 组件 - 移除与语言服务器相关的逻辑和状态管理- 简化了代码结构,提高了组件的可复用性和可维护性 - 优化了 AI 代码优化功能,增加了应用优化结果的按钮 -调整了样式,使界面更加直观
This commit is contained in:
parent
2cbc13e441
commit
57f52b67fa
@ -1,219 +1,101 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { DiffEditor } from "@monaco-editor/react";
|
import { DiffEditor } from "@monaco-editor/react";
|
||||||
import { optimizeCode } from "@/app/actions/ai-improve";
|
import { optimizeCode } from "@/app/actions/ai-improve";
|
||||||
import { OptimizeCodeInput } from "@/types/ai-improve";
|
import type { OptimizeCodeInput } from "@/types/ai-improve";
|
||||||
import dynamic from "next/dynamic";
|
import { CoreEditor } from "./core-editor"; // 引入你刚刚的组件
|
||||||
import { highlighter } from "@/lib/shiki";
|
// import { Loading } from "@/components/loading";
|
||||||
import type { editor } from "monaco-editor";
|
import type { LanguageServerConfig } from "@/generated/client";
|
||||||
import { Loading } from "@/components/loading";
|
|
||||||
import { shikiToMonaco } from "@shikijs/monaco";
|
|
||||||
import { useProblem } from "@/hooks/use-problem";
|
|
||||||
import type { Monaco } from "@monaco-editor/react";
|
|
||||||
import { useCallback, useEffect, useRef } from "react";
|
|
||||||
import { connectToLanguageServer } from "@/lib/language-server";
|
|
||||||
import type { MonacoLanguageClient } from "monaco-languageclient";
|
|
||||||
import { DefaultEditorOptionConfig } from "@/config/editor-option";
|
|
||||||
|
|
||||||
// 动态导入Monaco Editor
|
interface AIEditorWrapperProps {
|
||||||
const Editor = dynamic(
|
language?: string;
|
||||||
async () => {
|
value?: string;
|
||||||
await import("vscode");
|
path?: string;
|
||||||
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: () => <Loading />,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export function AIProblemEditor({
|
|
||||||
initialCode = "",
|
|
||||||
problemId = "",
|
|
||||||
onCodeChange
|
|
||||||
}: {
|
|
||||||
initialCode?: string;
|
|
||||||
problemId?: string;
|
problemId?: string;
|
||||||
onCodeChange?: (code: string) => void;
|
languageServerConfigs?: LanguageServerConfig[];
|
||||||
}) {
|
onChange?: (value: string) => void;
|
||||||
const {
|
className?: string;
|
||||||
editor,
|
}
|
||||||
setEditor,
|
|
||||||
setMarkers,
|
|
||||||
setWebSocket,
|
|
||||||
currentLang,
|
|
||||||
currentPath,
|
|
||||||
currentTheme,
|
|
||||||
currentValue,
|
|
||||||
changeValue,
|
|
||||||
currentEditorLanguageConfig,
|
|
||||||
currentLanguageServerConfig,
|
|
||||||
} = useProblem();
|
|
||||||
|
|
||||||
const monacoLanguageClientRef = useRef<MonacoLanguageClient | null>(null);
|
export const AIEditorWrapper = ({
|
||||||
|
language,
|
||||||
// 保持原有AI优化的状态
|
value,
|
||||||
const [showDiff, setShowDiff] = useState(false);
|
path,
|
||||||
|
problemId,
|
||||||
|
languageServerConfigs,
|
||||||
|
onChange,
|
||||||
|
// className,
|
||||||
|
}: AIEditorWrapperProps) => {
|
||||||
|
const [currentCode, setCurrentCode] = useState(value ?? "");
|
||||||
const [optimizedCode, setOptimizedCode] = useState("");
|
const [optimizedCode, setOptimizedCode] = useState("");
|
||||||
const [isOptimizing, setIsOptimizing] = useState(false);
|
const [isOptimizing, setIsOptimizing] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [showDiff, setShowDiff] = useState(false);
|
||||||
|
|
||||||
// 重用useProblem的状态管理
|
const handleCodeChange = useCallback((val: string) => {
|
||||||
const currentCode = currentValue || initialCode;
|
setCurrentCode(val);
|
||||||
|
onChange?.(val);
|
||||||
const handleCodeChange = useCallback((value: string | undefined) => {
|
}, [onChange]);
|
||||||
if (value !== undefined) {
|
|
||||||
changeValue(value);
|
|
||||||
if (onCodeChange) {
|
|
||||||
onCodeChange(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [onCodeChange, changeValue]);
|
|
||||||
|
|
||||||
// 保持原有LSP连接逻辑
|
|
||||||
const connectLSP = useCallback(async () => {
|
|
||||||
if (!(currentLang && editor)) return;
|
|
||||||
|
|
||||||
if (monacoLanguageClientRef.current) {
|
|
||||||
monacoLanguageClientRef.current.stop();
|
|
||||||
monacoLanguageClientRef.current = null;
|
|
||||||
setWebSocket(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentEditorLanguageConfig || !currentLanguageServerConfig) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { client: monacoLanguageClient, webSocket } = await connectToLanguageServer(
|
|
||||||
currentEditorLanguageConfig,
|
|
||||||
currentLanguageServerConfig
|
|
||||||
);
|
|
||||||
monacoLanguageClientRef.current = monacoLanguageClient;
|
|
||||||
setWebSocket(webSocket);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to connect to LSP:", error);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
currentEditorLanguageConfig,
|
|
||||||
currentLang,
|
|
||||||
currentLanguageServerConfig,
|
|
||||||
editor,
|
|
||||||
setWebSocket,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
connectLSP();
|
|
||||||
}, [connectLSP]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (monacoLanguageClientRef.current) {
|
|
||||||
monacoLanguageClientRef.current.stop();
|
|
||||||
monacoLanguageClientRef.current = null;
|
|
||||||
setWebSocket(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, [setWebSocket]);
|
|
||||||
|
|
||||||
const handleEditorWillMount = useCallback((monaco: Monaco) => {
|
|
||||||
shikiToMonaco(highlighter, monaco);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleOnMount = useCallback(
|
|
||||||
async (editor: editor.IStandaloneCodeEditor) => {
|
|
||||||
setEditor(editor);
|
|
||||||
await connectLSP();
|
|
||||||
},
|
|
||||||
[setEditor, connectLSP]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleEditorValidation = useCallback(
|
|
||||||
(markers: editor.IMarker[]) => {
|
|
||||||
setMarkers(markers);
|
|
||||||
},
|
|
||||||
[setMarkers]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOptimizeCode = useCallback(async () => {
|
|
||||||
if (!currentCode || !problemId) return;
|
|
||||||
|
|
||||||
|
const handleOptimize = useCallback(async () => {
|
||||||
|
if (!problemId || !currentCode) return;
|
||||||
setIsOptimizing(true);
|
setIsOptimizing(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const input: OptimizeCodeInput = {
|
const input: OptimizeCodeInput = {
|
||||||
code: currentCode,
|
code: currentCode,
|
||||||
problemId
|
problemId,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await optimizeCode(input);
|
const result = await optimizeCode(input);
|
||||||
setOptimizedCode(result.optimizedCode);
|
setOptimizedCode(result.optimizedCode);
|
||||||
setShowDiff(true);
|
setShowDiff(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError("代码优化失败,请重试");
|
setError("AI 优化失败,请稍后重试");
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} finally {
|
} finally {
|
||||||
setIsOptimizing(false);
|
setIsOptimizing(false);
|
||||||
}
|
}
|
||||||
}, [currentCode, problemId]);
|
}, [currentCode, problemId]);
|
||||||
|
|
||||||
|
const handleApplyOptimized = useCallback(() => {
|
||||||
|
setCurrentCode(optimizedCode);
|
||||||
|
onChange?.(optimizedCode);
|
||||||
|
setShowDiff(false);
|
||||||
|
}, [optimizedCode, onChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full w-full">
|
<div className="flex flex-col h-full w-full">
|
||||||
{/* 保持原有AI优化按钮 */}
|
<div className="flex items-center justify-between p-4">
|
||||||
<div className="flex justify-between items-center p-4">
|
|
||||||
<button
|
<button
|
||||||
onClick={handleOptimizeCode}
|
onClick={handleOptimize}
|
||||||
disabled={isOptimizing || !currentCode}
|
disabled={isOptimizing}
|
||||||
className="px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90"
|
className="px-4 py-2 bg-primary text-white rounded hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{isOptimizing ? "优化中..." : "AI优化代码"}
|
{isOptimizing ? "优化中..." : "AI优化代码"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showDiff && (
|
{showDiff && (
|
||||||
|
<div className="space-x-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowDiff(!showDiff)}
|
onClick={() => setShowDiff(false)}
|
||||||
className="px-4 py-2 bg-secondary text-secondary-foreground rounded-md"
|
className="px-4 py-2 bg-secondary text-white rounded"
|
||||||
>
|
>
|
||||||
{"隐藏对比"}
|
隐藏对比
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={handleApplyOptimized}
|
||||||
|
className="px-4 py-2 bg-green-500 text-white rounded"
|
||||||
|
>
|
||||||
|
应用优化结果
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<div className="p-3 bg-destructive/10 text-destructive rounded-md">
|
<div className="p-3 bg-red-100 text-red-600 rounded-md">{error}</div>
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex-grow overflow-hidden">
|
<div className="flex-grow overflow-hidden">
|
||||||
@ -221,30 +103,22 @@ export function AIProblemEditor({
|
|||||||
<DiffEditor
|
<DiffEditor
|
||||||
original={currentCode}
|
original={currentCode}
|
||||||
modified={optimizedCode}
|
modified={optimizedCode}
|
||||||
language={currentLang}
|
language={language}
|
||||||
theme={currentTheme}
|
theme="vs-dark"
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
options={{
|
options={{ readOnly: true, minimap: { enabled: false } }}
|
||||||
readOnly: true,
|
|
||||||
minimap: { enabled: false }
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Editor
|
<CoreEditor
|
||||||
language={currentLang}
|
language={language}
|
||||||
theme={currentTheme}
|
|
||||||
path={currentPath}
|
|
||||||
value={currentCode}
|
value={currentCode}
|
||||||
beforeMount={handleEditorWillMount}
|
path={path}
|
||||||
onMount={handleOnMount}
|
languageServerConfigs={languageServerConfigs}
|
||||||
onChange={handleCodeChange}
|
onChange={handleCodeChange}
|
||||||
onValidate={handleEditorValidation}
|
|
||||||
options={DefaultEditorOptionConfig}
|
|
||||||
loading={<Loading />}
|
|
||||||
className="h-full w-full"
|
className="h-full w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user