From 0dea7a42292413778c0fcb89a898c03dbe1b2973 Mon Sep 17 00:00:00 2001 From: cfngc4594 Date: Sun, 22 Jun 2025 11:23:40 +0800 Subject: [PATCH] refactor: optimize code --- messages/en.json | 8 + messages/zh.json | 8 + src/app/actions/analyze.ts | 33 +++- src/components/ai-optimized-editor.tsx | 163 ++++++++++++------ src/components/core-diff-editor.tsx | 70 ++++++++ src/components/problem-editor.tsx | 66 +++---- .../toolbar/actions/AIDisplayButton.tsx | 44 ----- .../toolbar/actions/AIOptimizeButton.tsx | 42 ----- .../toolbar/actions/optimize-button.tsx | 52 ++++++ .../toolbar/actions/view-toggle-button.tsx | 24 +++ .../code/components/toolbar/code-toolbar.tsx | 19 +- src/stores/problem-editor.ts | 28 ++- 12 files changed, 366 insertions(+), 191 deletions(-) create mode 100644 src/components/core-diff-editor.tsx delete mode 100644 src/features/problems/code/components/toolbar/actions/AIDisplayButton.tsx delete mode 100644 src/features/problems/code/components/toolbar/actions/AIOptimizeButton.tsx create mode 100644 src/features/problems/code/components/toolbar/actions/optimize-button.tsx create mode 100644 src/features/problems/code/components/toolbar/actions/view-toggle-button.tsx diff --git a/messages/en.json b/messages/en.json index e9c625d..1d96bfe 100644 --- a/messages/en.json +++ b/messages/en.json @@ -157,6 +157,14 @@ } }, "WorkspaceEditorHeader": { + "ViewToggleButton": { + "ShowCodeView": "Show code view", + "ShowDiffView": "Show diff view" + }, + "OptimizeButton":{ + "TooltipContent": "Optimize Code", + "Error": "Error occurred while optimizing code, please try again later." + }, "LspStatusButton": { "TooltipContent": "Language Server" }, diff --git a/messages/zh.json b/messages/zh.json index 245f0e6..1797913 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -157,6 +157,14 @@ } }, "WorkspaceEditorHeader": { + "ViewToggleButton": { + "ShowCodeView": "查看源代码", + "ShowDiffView": "查看优化代码" + }, + "OptimizeButton":{ + "TooltipContent": "优化代码", + "Error": "优化代码时出错,请稍后重试。" + }, "LspStatusButton": { "TooltipContent": "语言服务" }, diff --git a/src/app/actions/analyze.ts b/src/app/actions/analyze.ts index c35f8e9..1c2f1d4 100644 --- a/src/app/actions/analyze.ts +++ b/src/app/actions/analyze.ts @@ -66,7 +66,9 @@ export const analyzeComplexity = async ( return validationResult.data; }; -export const getAnalysis = async (submissionId: string):Promise => { +export const getAnalysis = async ( + submissionId: string +): Promise => { const session = await auth(); if (!session?.user?.id) { @@ -85,3 +87,32 @@ export const getAnalysis = async (submissionId: string):Promise => return analysis; }; + +export const optimizeCode = async (content: string): Promise => { + const model = openai("gpt-4o-mini"); + + const prompt = ` + Optimize the following code snippet for better performance, readability, and maintainability. + Provide ONLY the improved code without any explanations, comments, or additional text. + + Code to optimize: + \`\`\` + ${content} + \`\`\` + + Respond ONLY with the optimized code. Do not include any other text or markdown formatting like \`\`\`. + `; + + const messages: CoreMessage[] = [{ role: "user", content: prompt }]; + + try { + const response = await generateText({ + model: model, + messages: messages, + }); + return response.text.trim(); + } catch (error) { + console.error("Error optimizing code:", error); + throw new Error("Failed to optimize code"); + } +}; diff --git a/src/components/ai-optimized-editor.tsx b/src/components/ai-optimized-editor.tsx index 76b968b..adfd522 100644 --- a/src/components/ai-optimized-editor.tsx +++ b/src/components/ai-optimized-editor.tsx @@ -1,68 +1,127 @@ "use client"; +import { useCallback, useState } from "react"; import { DiffEditor } from "@monaco-editor/react"; import { optimizeCode } from "@/app/actions/ai-improve"; -import { useMonacoTheme } from "@/hooks/use-monaco-theme"; -import React, { useState, useEffect, useCallback } from "react"; -import { useProblemEditorStore } from "@/stores/problem-editor"; +import type { OptimizeCodeInput } from "@/types/ai-improve"; +import { CoreEditor } from "./core-editor"; // 引入你刚刚的组件 +// import { Loading } from "@/components/loading"; +import type { LanguageServerConfig } from "@/generated/client"; -export const AIEditorWrapper = () => { - const { - language, - value: originalCode, - setLoading, - AIgenerate, - LastOptimizedCode, - setLastOptimizedCode, - } = useProblemEditorStore(); +interface AIEditorWrapperProps { + language?: string; + value?: string; + path?: string; + problemId?: string; + languageServerConfigs?: LanguageServerConfig[]; + onChange?: (value: string) => void; + className?: string; +} - const [optimizedCode, setOptimizedCode] = useState(""); - const { theme } = useMonacoTheme(); +export const AIEditorWrapper = ({ + language, + value, + path, + problemId, + languageServerConfigs, + onChange, +}: // className, +AIEditorWrapperProps) => { + const [currentCode, setCurrentCode] = useState(value ?? ""); + const [optimizedCode, setOptimizedCode] = useState(""); + const [isOptimizing, setIsOptimizing] = useState(false); + const [error, setError] = useState(null); + const [showDiff, setShowDiff] = useState(false); + + const handleCodeChange = useCallback( + (val: string) => { + setCurrentCode(val); + onChange?.(val); + }, + [onChange] + ); const handleOptimize = useCallback(async () => { - setLoading(true); - try { - const res = await optimizeCode({ - code: originalCode, - error: "", - problemId: "", - }); - setOptimizedCode(res.optimizedCode); - setLastOptimizedCode(res.optimizedCode); - } catch (err) { - console.error("优化失败", err); - setOptimizedCode("// 优化失败,请稍后重试"); - } finally { - setLoading(false); - } - }, [originalCode, setLoading, setLastOptimizedCode]); + if (!problemId || !currentCode) return; + setIsOptimizing(true); + setError(null); - useEffect(() => { - if (AIgenerate) { - handleOptimize(); - } else if (LastOptimizedCode) { - setOptimizedCode(LastOptimizedCode); + try { + const input: OptimizeCodeInput = { + code: currentCode, + problemId, + }; + const result = await optimizeCode(input); + setOptimizedCode(result.optimizedCode); + setShowDiff(true); + } catch (err) { + setError("AI 优化失败,请稍后重试"); + console.error(err); + } finally { + setIsOptimizing(false); } - }, [AIgenerate, LastOptimizedCode, handleOptimize]); + }, [currentCode, problemId]); + + const handleApplyOptimized = useCallback(() => { + setCurrentCode(optimizedCode); + onChange?.(optimizedCode); + setShowDiff(false); + }, [optimizedCode, onChange]); return ( -
- {optimizedCode && ( -
- -
+
+
+ + + {showDiff && ( +
+ + +
+ )} +
+ + {error && ( +
{error}
)} + +
+ {showDiff ? ( + + ) : ( + + )} +
); }; diff --git a/src/components/core-diff-editor.tsx b/src/components/core-diff-editor.tsx new file mode 100644 index 0000000..9af4f9d --- /dev/null +++ b/src/components/core-diff-editor.tsx @@ -0,0 +1,70 @@ +"use client"; + +import dynamic from "next/dynamic"; +import type { editor } from "monaco-editor"; +import { useCallback, useRef } from "react"; +import { getHighlighter } from "@/lib/shiki"; +import { Loading } from "@/components/loading"; +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"; + +const MonacoEditor = dynamic( + async () => { + const react = await import("@monaco-editor/react"); + + return react.DiffEditor; + }, + { + ssr: false, + loading: () => , + } +); + +interface CoreDiffEditorProps { + language?: string; + original?: string; + modified?: string; + onEditorReady?: (editor: editor.IStandaloneDiffEditor) => void; + className?: string; +} + +export const CoreDiffEditor = ({ + language, + original, + modified, + onEditorReady, + className, +}: CoreDiffEditorProps) => { + const { theme } = useMonacoTheme(); + + const editorRef = useRef(null); + + const handleBeforeMount = useCallback((monaco: Monaco) => { + const highlighter = getHighlighter(); + shikiToMonaco(highlighter, monaco); + }, []); + + const handleOnMount = useCallback( + (editor: editor.IStandaloneDiffEditor) => { + editorRef.current = editor; + onEditorReady?.(editor); + }, + [onEditorReady] + ); + + return ( + } + className={className} + /> + ); +}; diff --git a/src/components/problem-editor.tsx b/src/components/problem-editor.tsx index 79947d4..1757d6e 100644 --- a/src/components/problem-editor.tsx +++ b/src/components/problem-editor.tsx @@ -2,8 +2,8 @@ import { useEffect } from "react"; import { CoreEditor } from "@/components/core-editor"; +import { CoreDiffEditor } from "@/components/core-diff-editor"; import { useProblemEditorStore } from "@/stores/problem-editor"; -import { AIEditorWrapper } from "@/components/ai-optimized-editor"; import type { LanguageServerConfig, Template } from "@/generated/client"; interface ProblemEditorProps { @@ -20,45 +20,53 @@ export const ProblemEditor = ({ const { language, value, + optimizedCode, path, setProblem, setValue, setEditor, + setDiffEditor, setLspWebSocket, setMarkers, - useAIEditor, - // setUseAIEditor + showDiffView, } = useProblemEditorStore(); useEffect(() => { setProblem(problemId, templates); }, [problemId, setProblem, templates]); - return ( -
- {!useAIEditor ? ( - <> - {/* setUseAIEditor(true)}*/} - {/*>*/} - {/* AI 优化代码*/} - {/**/} - - - ) : ( - - )} -
+ if (!optimizedCode) { + return ( + + ); + } + + return showDiffView ? ( + + ) : ( + ); }; diff --git a/src/features/problems/code/components/toolbar/actions/AIDisplayButton.tsx b/src/features/problems/code/components/toolbar/actions/AIDisplayButton.tsx deleted file mode 100644 index 8461109..0000000 --- a/src/features/problems/code/components/toolbar/actions/AIDisplayButton.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import { TooltipButton } from "@/components/tooltip-button"; -import { useProblemEditorStore } from "@/stores/problem-editor"; -import { ArrowLeftRight, LoaderCircleIcon, Undo2Icon } from "lucide-react"; - -export const AIDisplayButton = () => { - const { useAIEditor, setUseAIEditor, setAIgenerate, loading } = - useProblemEditorStore(); - - const handleClick = () => { - setAIgenerate(false); - if (!loading) { - setUseAIEditor(!useAIEditor); - } - }; - - const tooltipContent = loading - ? "AI 正在优化中…" - : useAIEditor - ? "返回原始编辑器" - : "查看 AI 优化代码"; - - return ( - - {loading ? ( - - ); -}; diff --git a/src/features/problems/code/components/toolbar/actions/AIOptimizeButton.tsx b/src/features/problems/code/components/toolbar/actions/AIOptimizeButton.tsx deleted file mode 100644 index 15cdc70..0000000 --- a/src/features/problems/code/components/toolbar/actions/AIOptimizeButton.tsx +++ /dev/null @@ -1,42 +0,0 @@ -"use client"; - -import { Wand2Icon } from "lucide-react"; -import { TooltipButton } from "@/components/tooltip-button"; -//import { LoaderCircleIcon, Undo2Icon } from "lucide-react"; -import { useProblemEditorStore } from "@/stores/problem-editor"; - -export const AIOptimizeButton = () => { - const { useAIEditor, setUseAIEditor, setAIgenerate, loading } = - useProblemEditorStore(); - - const handleClick = () => { - setAIgenerate(true); - if (!loading) { - setUseAIEditor(!useAIEditor); - } - }; - - // ? "AI 正在优化中…" - // : useAIEditor - // ? "返回原始编辑器" - // : "使用 AI 优化代码"; - - const tooltipContent = "使用 AI 优化代码"; // 仅保留默认提示内容 - - return ( - - {loading ? // className="opacity-60 animate-spin" // - ); -}; diff --git a/src/features/problems/code/components/toolbar/actions/optimize-button.tsx b/src/features/problems/code/components/toolbar/actions/optimize-button.tsx new file mode 100644 index 0000000..d7e5372 --- /dev/null +++ b/src/features/problems/code/components/toolbar/actions/optimize-button.tsx @@ -0,0 +1,52 @@ +"use client"; + +import { useState } from "react"; +import { useTranslations } from "next-intl"; +import { optimizeCode } from "@/app/actions/analyze"; +import { LoaderCircleIcon, Wand2Icon } from "lucide-react"; +import { TooltipButton } from "@/components/tooltip-button"; +import { useProblemEditorStore } from "@/stores/problem-editor"; +import { useProblemEditorActions } from "@/features/problems/code/hooks/use-problem-editor-actions"; + +export const OptimizeButton = () => { + const t = useTranslations("WorkspaceEditorHeader.OptimizeButton"); + const { value, setOptimizedCode } = useProblemEditorStore(); + const { canExecute } = useProblemEditorActions(); + // const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); + + const handleClick = async () => { + // setError(null); + setIsLoading(true); + setOptimizedCode(""); + try { + const optimizedCode = await optimizeCode(value); + setOptimizedCode(optimizedCode); + } catch (error) { + console.error("Error analyzing complexity:", error); + setOptimizedCode(""); + // setError(t("Error")); + } finally { + setIsLoading(false); + } + }; + + return ( + + {isLoading ? ( + + ); +}; diff --git a/src/features/problems/code/components/toolbar/actions/view-toggle-button.tsx b/src/features/problems/code/components/toolbar/actions/view-toggle-button.tsx new file mode 100644 index 0000000..f154253 --- /dev/null +++ b/src/features/problems/code/components/toolbar/actions/view-toggle-button.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { CodeXmlIcon, GitCompareIcon } from "lucide-react"; +import { TooltipButton } from "@/components/tooltip-button"; +import { useProblemEditorStore } from "@/stores/problem-editor"; + +export const ViewToggleButton = () => { + const t = useTranslations("WorkspaceEditorHeader.ViewToggleButton"); + const { showDiffView, toggleView } = useProblemEditorStore(); + + return ( + + {showDiffView ? ( + + ); +}; diff --git a/src/features/problems/code/components/toolbar/code-toolbar.tsx b/src/features/problems/code/components/toolbar/code-toolbar.tsx index fdfa3f5..15e8a7a 100644 --- a/src/features/problems/code/components/toolbar/code-toolbar.tsx +++ b/src/features/problems/code/components/toolbar/code-toolbar.tsx @@ -1,3 +1,5 @@ +"use client"; + import { cn } from "@/lib/utils"; import { CopyButton, @@ -7,16 +9,19 @@ import { ResetButton, UndoButton, } from "@/features/problems/code/components/toolbar"; -import { AnalyzeButton } from "./actions/analyze-button"; -import { LspConnectionIndicator } from "./controls/lsp-connection-indicator"; -import { AIDisplayButton } from "@/features/problems/code/components/toolbar/actions/AIDisplayButton"; -import { AIOptimizeButton } from "@/features/problems/code/components/toolbar/actions/AIOptimizeButton"; +import { useProblemEditorStore } from "@/stores/problem-editor"; +import { AnalyzeButton } from "@/features/problems/code/components/toolbar/actions/analyze-button"; +import { OptimizeButton } from "@/features/problems/code/components/toolbar/actions/optimize-button"; +import { ViewToggleButton } from "@/features/problems/code/components/toolbar/actions/view-toggle-button"; +import { LspConnectionIndicator } from "@/features/problems/code/components/toolbar/controls/lsp-connection-indicator"; interface CodeToolbarProps { className?: string; } -export const CodeToolbar = async ({ className }: CodeToolbarProps) => { +export const CodeToolbar = ({ className }: CodeToolbarProps) => { + const { optimizedCode } = useProblemEditorStore(); + return (
{
- - + {optimizedCode && } + diff --git a/src/stores/problem-editor.ts b/src/stores/problem-editor.ts index e16c306..fd7ae2f 100644 --- a/src/stores/problem-editor.ts +++ b/src/stores/problem-editor.ts @@ -12,28 +12,26 @@ type ProblemEditorState = { problem: Problem | null; language: Language; value: string; + optimizedCode: string; path: string; editor: editor.IStandaloneCodeEditor | null; + diffEditor: editor.IStandaloneDiffEditor | null; lspWebSocket: WebSocket | null; markers: editor.IMarker[]; - useAIEditor: boolean; - loading: boolean; - AIgenerate: boolean; - LastOptimizedCode: string; + showDiffView: boolean; }; type ProblemEditorAction = { setProblem: (problemId: string, templates: Template[]) => void; setLanguage: (language: Language) => void; setValue: (value: string) => void; + setOptimizedCode: (value: string) => void; setPath: (path: string) => void; setEditor: (editor: editor.IStandaloneCodeEditor) => void; + setDiffEditor: (diffEditor: editor.IStandaloneDiffEditor) => void; setLspWebSocket: (lspWebSocket: WebSocket) => void; setMarkers: (markers: editor.IMarker[]) => void; - setUseAIEditor: (flag: boolean) => void; - setLoading: (flag: boolean) => void; - setAIgenerate: (flag: boolean) => void; - setLastOptimizedCode: (code: string) => void; + toggleView: () => void; }; type ProblemEditorStore = ProblemEditorState & ProblemEditorAction; @@ -42,18 +40,13 @@ export const useProblemEditorStore = create((set, get) => ({ problem: null, language: Language.c, value: "", + optimizedCode: "", path: "", editor: null, + diffEditor: null, lspWebSocket: null, markers: [], - useAIEditor: false, - loading: false, - AIgenerate: false, - LastOptimizedCode: "", - setLastOptimizedCode: (code) => set({ LastOptimizedCode: code }), - setAIgenerate: (flag) => set({ AIgenerate: flag }), - setLoading: (loading) => set({ loading }), - setUseAIEditor: (loading) => set({ useAIEditor: loading }), + showDiffView: false, setProblem: (problemId, templates) => { const language = getLanguage(problemId); const value = getValue(problemId, language, templates); @@ -80,10 +73,13 @@ export const useProblemEditorStore = create((set, get) => ({ } set({ value }); }, + setOptimizedCode: (optimizedCode) => set({ optimizedCode }), setPath: (path) => set({ path }), setEditor: (editor) => set({ editor }), + setDiffEditor: (diffEditor) => set({ diffEditor }), setLspWebSocket: (lspWebSocket) => set({ lspWebSocket }), setMarkers: (markers) => set({ markers }), + toggleView: () => set((state) => ({ showDiffView: !state.showDiffView })), })); const getStoredItem = (