feat: add AI research chatbot with confirmed business tools
This commit is contained in:
+93
-5
@@ -23,6 +23,8 @@ import type { Account, Job } from "./types";
|
||||
import { AccountPage } from "./pages/AccountPage";
|
||||
import { AlphaPage } from "./pages/AlphaPage";
|
||||
import { JobPanel } from "./components/JobPanel";
|
||||
import { ChatPanel } from "./ai/ChatPanel";
|
||||
import type { PageContext, UIAction } from "./ai/types";
|
||||
|
||||
export default function App() {
|
||||
const [authenticated, setAuthenticated] = useState<boolean | null>(null);
|
||||
@@ -34,6 +36,35 @@ export default function App() {
|
||||
const [showJobs, setShowJobs] = useState(false);
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
const [pollError, setPollError] = useState("");
|
||||
const [chatOpen, setChatOpen] = useState(false);
|
||||
const [chatWidth, setChatWidth] = useState(420);
|
||||
const [viewport, setViewport] = useState(window.innerWidth);
|
||||
const [alphaContext, setAlphaContext] = useState<PageContext>({
|
||||
page: "alphas",
|
||||
});
|
||||
const [aiAction, setAIAction] = useState<UIAction | null>(null);
|
||||
const chatOffset = viewport >= 1440 && chatOpen ? chatWidth : 0;
|
||||
const focusBusiness = useCallback(() => {
|
||||
if (window.innerWidth < 1440) setChatOpen(false);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const resize = () => setViewport(window.innerWidth);
|
||||
window.addEventListener("resize", resize);
|
||||
return () => window.removeEventListener("resize", resize);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
document.documentElement.style.setProperty(
|
||||
"--chat-space",
|
||||
`${chatOffset}px`,
|
||||
);
|
||||
document.documentElement.style.setProperty(
|
||||
"--chat-width",
|
||||
`${chatWidth}px`,
|
||||
);
|
||||
return () => {
|
||||
document.documentElement.style.setProperty("--chat-space", "0px");
|
||||
};
|
||||
}, [chatOffset, chatWidth]);
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
const [nextAccount, nextJobs] = await Promise.all([
|
||||
@@ -56,6 +87,8 @@ export default function App() {
|
||||
setAuthenticated(false);
|
||||
setAccount(null);
|
||||
setJobs([]);
|
||||
setChatOpen(false);
|
||||
setShowJobs(false);
|
||||
};
|
||||
window.addEventListener("session-expired", expired);
|
||||
const hash = () =>
|
||||
@@ -103,6 +136,12 @@ export default function App() {
|
||||
try {
|
||||
await post("/auth/logout");
|
||||
setAuthenticated(false);
|
||||
setChatOpen(false);
|
||||
setShowJobs(false);
|
||||
setAccount(null);
|
||||
setJobs([]);
|
||||
setAIAction(null);
|
||||
setAlphaContext({ page: "alphas" });
|
||||
} catch (e) {
|
||||
Toast.error((e as Error).message);
|
||||
}
|
||||
@@ -174,11 +213,21 @@ export default function App() {
|
||||
<Button
|
||||
theme="borderless"
|
||||
icon={<IconBell />}
|
||||
onClick={() => setShowJobs(true)}
|
||||
onClick={() => {
|
||||
focusBusiness();
|
||||
setShowJobs(true);
|
||||
}}
|
||||
>
|
||||
同步任务
|
||||
</Button>
|
||||
</Badge>
|
||||
<Button
|
||||
aria-label="切换研究助手"
|
||||
theme={chatOpen ? "light" : "borderless"}
|
||||
onClick={() => setChatOpen(!chatOpen)}
|
||||
>
|
||||
AI 助手
|
||||
</Button>
|
||||
<div className="top-divider" />
|
||||
<Avatar size="small" color="purple">
|
||||
{account?.display_name.slice(0, 1) || "研"}
|
||||
@@ -201,25 +250,35 @@ export default function App() {
|
||||
description={`后台状态暂时不可用:${pollError}`}
|
||||
/>
|
||||
)}
|
||||
{page === "account" ? (
|
||||
<div hidden={page !== "account"}>
|
||||
<AccountPage
|
||||
account={account}
|
||||
onChange={actionDone}
|
||||
onTask={taskCreated}
|
||||
/>
|
||||
) : (
|
||||
</div>
|
||||
<div hidden={page !== "alphas"}>
|
||||
<AlphaPage
|
||||
taskPanelOpen={showJobs}
|
||||
account={account}
|
||||
version={`${refreshKey}:${completedVersion}`}
|
||||
onTask={taskCreated}
|
||||
onAccount={() => changePage("account")}
|
||||
active={page === "alphas"}
|
||||
overlaySuspended={
|
||||
page !== "alphas" || (viewport < 1440 && chatOpen)
|
||||
}
|
||||
chatOffset={chatOffset}
|
||||
onContext={setAlphaContext}
|
||||
action={aiAction}
|
||||
onOverlay={focusBusiness}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<JobPanel
|
||||
visible={showJobs}
|
||||
visible={showJobs && !(viewport < 1440 && chatOpen)}
|
||||
chatOffset={chatOffset}
|
||||
jobs={jobs}
|
||||
timezone={account?.timezone}
|
||||
onClose={() => setShowJobs(false)}
|
||||
@@ -229,6 +288,35 @@ export default function App() {
|
||||
changePage("account");
|
||||
}}
|
||||
/>
|
||||
{!chatOpen && (
|
||||
<Button
|
||||
className="ai-launcher"
|
||||
aria-label="打开研究助手"
|
||||
theme="solid"
|
||||
onClick={() => setChatOpen(true)}
|
||||
>
|
||||
AI 研究助手
|
||||
</Button>
|
||||
)}
|
||||
<ChatPanel
|
||||
jobs={jobs}
|
||||
open={chatOpen}
|
||||
width={chatWidth}
|
||||
onWidth={setChatWidth}
|
||||
onClose={() => setChatOpen(false)}
|
||||
context={page === "alphas" ? alphaContext : { page: "account" }}
|
||||
timezone={account?.timezone}
|
||||
onSettings={() => {
|
||||
focusBusiness();
|
||||
changePage("account");
|
||||
}}
|
||||
onChanged={actionDone}
|
||||
onAction={(action) => {
|
||||
focusBusiness();
|
||||
changePage("alphas");
|
||||
setAIAction(action);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</LocaleProvider>
|
||||
|
||||
Reference in New Issue
Block a user