init: initial commit

This commit is contained in:
Blizzard
2026-04-07 17:35:09 +08:00
commit 680ecc320f
129 changed files with 10562 additions and 0 deletions
@@ -0,0 +1,179 @@
import { Shield, UploadCloud, FileCheck, Loader2, RefreshCw } from 'lucide-react';
import { useState } from 'react';
import { useAppStore, useCurrentProject } from '../../stores/useAppStore';
import { useUIStore } from '../../stores/useUIStore';
import { useChatStore } from '../../stores/useChatStore';
import { ParseDeliveryStandard } from '../../../bindings/engimind/internal/parser/parseservice.js';
import { StreamTemplateDirectory } from '../../../bindings/engimind/internal/chat/chatservice.js';
import { Events } from '@wailsio/runtime';
export function TemplateCard() {
const currentProject = useCurrentProject();
const { isParsingTemplate, hasNewTemplatePending, setParsingTemplate, setNewTemplatePending, setEditingOutline } = useUIStore();
const setProjects = useAppStore(s => s.setProjects);
const currentProjectId = useAppStore(s => s.currentProjectId);
const [pendingMarkdown, setPendingMarkdown] = useState('');
const startTemplateParse = async () => {
if (!pendingMarkdown) return;
try {
setParsingTemplate(true);
setNewTemplatePending(false);
const activeModelId = useAppStore.getState().activeModelId;
if (!activeModelId) {
alert("提示:请先在配置中心连接一个有效的大语言模型提供商,否则无法进行文件解析!");
setParsingTemplate(false);
setNewTemplatePending(true);
return;
}
const cs = useChatStore.getState();
const userMsgId = Date.now();
const assistantMsgId = userMsgId + 1;
const msgIdStr = assistantMsgId.toString();
cs.addMessage({ id: userMsgId, role: 'user', content: '我上传了一份工程交付文档。请帮我深度解析并归纳出标准大纲结构。' });
cs.addMessage({ id: assistantMsgId, role: 'assistant', content: '', status: 'processing' });
let unSub = Events.On("chat_stream_" + msgIdStr, (e: any) => {
const fullText = Array.isArray(e.data) ? e.data[0] : e.data;
if (typeof fullText === 'string') {
const msg = cs.messages.find(m => m.id === assistantMsgId);
if (msg && fullText.length > msg.content.length) {
cs.updateMessage(assistantMsgId, { content: fullText });
}
}
});
// Invoke LLM to extract standard chapters
let jsonStr = await StreamTemplateDirectory(pendingMarkdown, activeModelId, msgIdStr);
unSub();
cs.updateMessage(assistantMsgId, { status: 'success' });
let jsonStrClean = jsonStr.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
jsonStrClean = jsonStrClean.replace(/```json/g, '').replace(/```/g, '').trim();
const startIdx = jsonStrClean.indexOf('[');
const endIdx = jsonStrClean.lastIndexOf(']');
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
jsonStrClean = jsonStrClean.substring(startIdx, endIdx + 1);
}
let parsedChapters;
try {
parsedChapters = JSON.parse(jsonStrClean);
} catch (err) {
console.error("LLM json decode failed:", jsonStrClean);
alert("模型解析出的结果不符合要求格式,请重试!\n模型原始输出片段:" + jsonStrClean.substring(0, 100) + "...");
setParsingTemplate(false);
setNewTemplatePending(true);
return;
}
if (!Array.isArray(parsedChapters)) {
parsedChapters = parsedChapters.chapters || parsedChapters.data || [];
if (!Array.isArray(parsedChapters)) {
parsedChapters = [parsedChapters];
}
}
const chapters = parsedChapters.map((c: any, i: number) => ({
id: c.id || `generated-${Date.now()}-${i}`,
title: c.title || `${i+1}节 内容生成`,
status: 'idle',
progress: 0,
content: c.content || ''
}));
// Update project directory globally
setProjects(prev => prev.map(p => {
if (p.id !== currentProjectId) return p;
return {
...p,
activeTemplate: {
name: 'AI 深层解析大纲', version: 'v1.0 (Auto)',
chapters,
},
};
}));
setParsingTemplate(false);
setPendingMarkdown('');
setEditingOutline(true);
} catch (err: any) {
console.error(err);
alert("模型解析遇到错误:" + (err.message || err));
setParsingTemplate(false);
setNewTemplatePending(true);
}
};
const handleUploadClick = async () => {
if (currentProject.activeTemplate.chapters && currentProject.activeTemplate.chapters.length > 0) {
if (!window.confirm("当前已经存在解析好的交付标准目录,确定要重新上传并替换吗?已有的内容和结构将会被覆盖。")) {
return;
}
}
try {
const markdownContent = await ParseDeliveryStandard();
if (markdownContent) {
setPendingMarkdown(markdownContent);
setNewTemplatePending(true);
}
} catch (error: any) {
alert("读取或转换文件内容失败: " + (error.message || error));
}
};
return (
<div className="p-3 mx-2 my-4 mt-auto rounded-[14px] bg-[#F1EFEA]">
<div className="flex items-center justify-between mb-3 text-[10px] font-medium text-[#8E8B83] uppercase tracking-[0.15em] px-1">
<div className="flex items-center gap-2"><Shield size={12} className="text-[#2D2D2D]" /> </div>
<button
onClick={handleUploadClick}
className="p-1 hover:bg-[#EBE9E4] rounded-md text-[#8E8B83] hover:text-[#2D2D2D] transition-colors"
title="上传交付标准"
>
<UploadCloud size={14} />
</button>
</div>
<div className={`relative p-3 rounded-xl border transition-all duration-300 bg-[#FFFFFF] border-[#E5E4E0] shadow-[0_2px_10px_-4px_rgba(0,0,0,0.05)] ${
isParsingTemplate ? 'border-[#E5E4E0] animate-pulse ring-1 ring-[#E5E4E0]' : ''
}`}>
{isParsingTemplate ? (
<div className="flex flex-col items-center py-1 gap-2">
<Loader2 size={16} className="text-[#D97757] animate-spin" />
<span className="text-[9px] font-medium text-[#D97757] uppercase">AI ...</span>
</div>
) : hasNewTemplatePending ? (
<button
onClick={startTemplateParse}
className="w-full py-1.5 bg-[#D97757] text-white rounded-lg text-[11px] font-medium transition-all hover:bg-[#C86444] shadow-sm transform hover:scale-[1.01]"
>
</button>
) : (
<div className="flex items-start gap-3">
<FileCheck size={16} className="text-[#8E8B83] mt-0.5" />
<div className="min-w-0 flex-1">
<p className="text-[12px] font-medium text-[#2D2D2D] truncate text-left">{currentProject.activeTemplate.name}</p>
<div className="flex items-center gap-2 mt-1">
<span className="inline-flex items-center gap-1.5 text-[9px] font-mono text-[#8E8B83]">
<span className="w-1.5 h-1.5 rounded-full bg-[#D97757]" />
{currentProject.activeTemplate.version} ACTIVE
</span>
<RefreshCw size={10} className="text-[#C2C0B8]" />
</div>
</div>
</div>
)}
</div>
</div>
);
}