Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,5 @@ export const api = {
listHistory: (limit = 50) => req<GenerationRecord[]>(`/api/history?limit=${limit}`),
clearHistory: () => req<void>("/api/history", { method: "DELETE" }),
deleteHistory: (id: number) => req<void>(`/api/history/${id}`, { method: "DELETE" }),
getGenerationSvg: (id: number) => req<{ svg: string }>(`/api/history/${id}/svg`),
};
21 changes: 14 additions & 7 deletions frontend/src/pages/Generate.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState, useCallback } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { python } from "@codemirror/lang-python";
import { vscodeDark } from "@uiw/codemirror-theme-vscode";
Expand All @@ -24,6 +24,7 @@ export default function GeneratePage() {
const [showDiff, setShowDiff] = useState(false);
const svgRef = useRef<HTMLDivElement>(null);
const abortRef = useRef<AbortController | null>(null);
const userModifiedRef = useRef(false); // 追踪用户是否手动修改了 systemOverride

useEffect(() => {
return () => {
Expand All @@ -49,17 +50,23 @@ export default function GeneratePage() {
// 当用户选择模板,把 user_template 灌入编辑框
const tpl = useMemo(() => templates.find((t) => t.id === templateId) || null, [templates, templateId]);
useEffect(() => {
// 切换模板时重置用户修改标记
userModifiedRef.current = false;
if (tpl?.user_template && !userPrompt) {
setUserPrompt(tpl.user_template);
}
if (tpl?.system_prompt) setSystemOverride(tpl.system_prompt);
// 仅在用户未手动修改时,才将 systemOverride 同步为模板默认值
if (tpl?.system_prompt && !userModifiedRef.current) {
setSystemOverride(tpl.system_prompt);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [templateId]);

// 模板切换时刷新 systemOverride(当用户没自定义时跟模板走)
useEffect(() => {
if (tpl) setSystemOverride(tpl.system_prompt || "");
}, [tpl]);
// 用户手动编辑 systemOverride 时标记为已修改
const handleSystemOverrideChange = useCallback((value: string) => {
userModifiedRef.current = true;
setSystemOverride(value);
}, []);

async function run() {
if (!modelId) {
Expand Down Expand Up @@ -219,7 +226,7 @@ export default function GeneratePage() {
rows={5}
className="w-full input font-mono text-xs mt-2"
value={systemOverride}
onChange={(e) => setSystemOverride(e.target.value)}
onChange={(e) => handleSystemOverrideChange(e.target.value)}
placeholder="留空则使用后端默认科研绘图约束;选择模板时默认填入模板的 system_prompt"
/>
)}
Expand Down
43 changes: 42 additions & 1 deletion frontend/src/pages/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export default function HistoryPage() {
const [items, setItems] = useState<GenerationRecord[]>([]);
const [active, setActive] = useState<GenerationRecord | null>(null);
const [msg, setMsg] = useState("");
const [svgContent, setSvgContent] = useState<string | null>(null);
const [svgLoading, setSvgLoading] = useState(false);
const [svgError, setSvgError] = useState("");

async function load() {
try {
Expand All @@ -21,16 +24,33 @@ export default function HistoryPage() {
load();
}, []);

async function selectRecord(r: GenerationRecord) {
setActive(r);
setSvgContent(null);
setSvgError("");
setSvgLoading(true);
try {
const result = await api.getGenerationSvg(r.id);
setSvgContent(result.svg || null);
} catch (e) {
setSvgError("SVG 加载失败:" + (e as Error).message);
} finally {
setSvgLoading(false);
}
}

async function clearAll() {
if (!confirm("清空全部历史?")) return;
await api.clearHistory();
setItems([]);
setActive(null);
setSvgContent(null);
}

async function remove(id: number) {
await api.deleteHistory(id);
setActive(null);
setSvgContent(null);
await load();
}

Expand All @@ -52,7 +72,7 @@ export default function HistoryPage() {
items.map((r) => (
<li
key={r.id}
onClick={() => setActive(r)}
onClick={() => selectRecord(r)}
className={`p-3 border rounded cursor-pointer text-sm ${
active?.id === r.id ? "bg-slate-100 border-slate-400" : "hover:bg-slate-50"
}`}
Expand Down Expand Up @@ -104,6 +124,27 @@ export default function HistoryPage() {
editable={false}
/>
</div>
<div className="bg-white border border-slate-200 rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="font-semibold text-sm">SVG 预览</div>
</div>
{svgError && (
<div className="text-sm text-rose-600 bg-rose-50 border border-rose-200 rounded p-2 mb-2">{svgError}</div>
)}
<div className="border border-slate-200 rounded bg-slate-50 flex items-center justify-center min-h-[320px] p-3">
{svgLoading ? (
<div className="text-slate-400 text-sm">SVG 加载中…</div>
) : svgContent ? (
<div
className="w-full h-full flex items-center justify-center"
dangerouslySetInnerHTML={{ __html: svgContent }}
style={{ maxHeight: "70vh" }}
/>
) : (
<div className="text-slate-400 text-sm">暂无 SVG 内容</div>
)}
</div>
</div>
</div>
) : (
<div className="text-slate-500 text-sm">点击左侧查看详情。</div>
Expand Down