Initial commit: OmniAI Web Frontend

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:38:01 +08:00
commit bedee3ba8d
183 changed files with 94805 additions and 0 deletions
+276
View File
@@ -0,0 +1,276 @@
import {
ApartmentOutlined,
AppstoreOutlined,
BgColorsOutlined,
CodeOutlined,
DatabaseOutlined,
DownOutlined,
FileTextOutlined,
LoginOutlined,
PaperClipOutlined,
RobotOutlined,
RocketOutlined,
SendOutlined,
ThunderboltOutlined,
} from "@ant-design/icons";
import { useRef, useState } from "react";
import WorkspacePageShell from "../../components/WorkspacePageShell";
import type { WebGenerationPreviewTask } from "../../types";
interface AgentPageProps {
tasks: WebGenerationPreviewTask[];
isAuthenticated: boolean;
onCreateTask: (input: {
title: string;
type: WebGenerationPreviewTask["type"];
prompt: string;
}) => Promise<WebGenerationPreviewTask>;
onRequireLogin: (input: {
title: string;
type: WebGenerationPreviewTask["type"];
prompt: string;
}) => void;
onOpenLogin: () => void;
}
const agentModes = [
{
id: "task",
label: "任务拆解",
icon: <FileTextOutlined />,
placeholder: "拆解「新品发布会全流程」",
},
{
id: "workflow",
label: "流程编排",
icon: <ApartmentOutlined />,
placeholder: "规划「内容生产自动化链路」",
},
{
id: "data",
label: "数据分析",
icon: <DatabaseOutlined />,
placeholder: "分析「本周转化异常原因」",
},
{
id: "code",
label: "代码执行",
icon: <CodeOutlined />,
placeholder: "生成「落地页 A/B 测试脚本」",
},
{
id: "content",
label: "内容生成",
icon: <BgColorsOutlined />,
placeholder: "创作「品牌短片分镜脚本」",
},
{
id: "agent",
label: "智能体编排",
icon: <RobotOutlined />,
placeholder: "启动「多 Agent 调研与交付」",
},
];
const quickStarts = ["「新品发布」全链路运营", "「销售日报」自动分析", "「竞品监控」每周报告"];
function getTaskSourceLabel(task: WebGenerationPreviewTask): string | null {
if (task.source === "server") return "正式";
if (task.source === "preview") return "预览";
if (task.source === "mock-fallback") return "示例";
return null;
}
function AgentPage({
tasks,
isAuthenticated,
onCreateTask,
onRequireLogin,
onOpenLogin,
}: AgentPageProps) {
const composerRef = useRef<HTMLTextAreaElement>(null);
const [activeMode, setActiveMode] = useState(agentModes[1].id);
const [prompt, setPrompt] = useState("让 Omni Agent 帮我规划「新品发布会全流程」");
const [isRunning, setIsRunning] = useState(false);
const [notice, setNotice] = useState("选择一个 Agent 模式,输入目标后即可开始。");
const selectedMode = agentModes.find((item) => item.id === activeMode) ?? agentModes[0];
const recentTasks = tasks.slice(0, 3);
const focusComposer = () => {
composerRef.current?.focus();
};
const handleQuickStart = (value: string) => {
setPrompt(`让 Omni Agent 帮我规划${value}`);
setNotice("已载入快速启动模板,可继续补充目标、资料或约束。");
window.requestAnimationFrame(focusComposer);
};
const handleRun = async () => {
const trimmedPrompt = prompt.trim();
if (!trimmedPrompt || isRunning) {
setNotice(trimmedPrompt ? "当前 Agent 正在创建任务。" : "先输入一个目标,Agent 会自动拆解任务。");
return;
}
const taskInput = {
title: `${selectedMode.label} Agent`,
type: "agent" as const,
prompt: trimmedPrompt,
};
if (!isAuthenticated) {
onRequireLogin(taskInput);
setNotice("登录后即可运行 Agent,并保存执行记录。");
return;
}
setIsRunning(true);
setNotice("正在拆解目标、选择工具并创建执行队列...");
try {
await onCreateTask(taskInput);
setNotice("Agent 任务已加入队列,执行记录会同步到最近运行。");
} catch (error) {
setNotice(error instanceof Error ? error.message : "Agent 任务创建失败,请稍后重试。");
} finally {
setIsRunning(false);
}
};
return (
<WorkspacePageShell title="Agent" fullWidth className="agent-page page-motion">
<section className="agent-experience" aria-label="Omni Agent">
<header className="agent-nav">
<a className="agent-logo" href="#/workbench" aria-label="返回首页">
<span className="agent-logo__mark" aria-hidden="true" />
<span>Omni Agent</span>
</a>
<nav className="agent-nav__links" aria-label="Agent 页面导航">
<button type="button" onClick={focusComposer}></button>
<button type="button" onClick={focusComposer}></button>
</nav>
<div className="agent-nav__actions">
<button
type="button"
className="agent-nav__login"
onClick={onOpenLogin}
disabled={isAuthenticated}
>
<LoginOutlined />
{isAuthenticated ? "已登录" : "登录"}
</button>
<button type="button" className="agent-nav__start" onClick={focusComposer}>
</button>
</div>
</header>
<main className="agent-main">
<section className="agent-hero" aria-label="Agent 任务输入">
<h1> </h1>
<div className="agent-mode-selector" aria-label="Agent 能力模式">
{agentModes.map((mode) => (
<button
key={mode.id}
type="button"
className={`agent-mode${mode.id === activeMode ? " is-active" : ""}`}
aria-pressed={mode.id === activeMode}
onClick={() => {
setActiveMode(mode.id);
setNotice(`已切换到${mode.label}模式。`);
}}
>
<span className="agent-mode__glyph">{mode.icon}</span>
<span>{mode.label}</span>
</button>
))}
</div>
<section className="agent-composer" aria-label="Agent 指令输入">
<textarea
ref={composerRef}
value={prompt}
placeholder={`让 Omni Agent 帮你${selectedMode.placeholder}`}
onChange={(event) => setPrompt(event.target.value)}
onKeyDown={(event) => {
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
event.preventDefault();
void handleRun();
}
}}
/>
<div className="agent-composer__footer">
<div className="agent-composer__controls" aria-label="输入设置">
<button type="button" className="agent-tool-icon" aria-label="上传附件">
<PaperClipOutlined />
</button>
<button type="button" className="agent-tool-pill">
<ThunderboltOutlined />
<DownOutlined />
</button>
<button type="button" className="agent-tool-icon" aria-label="工具集">
<AppstoreOutlined />
</button>
</div>
<button
type="button"
className="agent-run-button"
disabled={isRunning}
onClick={() => void handleRun()}
>
{isRunning ? <RocketOutlined /> : <SendOutlined />}
{isRunning ? "运行中" : "运行"}
</button>
</div>
</section>
<p className="agent-status" aria-live="polite">{notice}</p>
<section className="agent-quick-start" aria-label="快速开始">
<span></span>
<div>
{quickStarts.map((item) => (
<button key={item} type="button" onClick={() => handleQuickStart(item)}>
{item}
</button>
))}
</div>
</section>
</section>
{recentTasks.length > 0 ? (
<section className="agent-recent" aria-label="最近运行">
<div className="agent-recent__head">
<span>Recent Runs</span>
<strong></strong>
</div>
<div className="agent-recent__grid">
{recentTasks.map((task) => {
const sourceLabel = getTaskSourceLabel(task);
return (
<article key={task.id} className="agent-run-card">
<div>
<strong>{task.title}</strong>
{sourceLabel ? <span>{sourceLabel}</span> : null}
</div>
<p>{task.prompt}</p>
<small>{task.status} / {task.progress}%</small>
</article>
);
})}
</div>
</section>
) : null}
</main>
</section>
</WorkspacePageShell>
);
}
export default AgentPage;