Merge: 解决 ScheduledTaskRunDetailPage 合并冲突

保留 HEAD 的编辑功能 import(useCallback、Edit3、Loader2、Save、X),
同时采用 b78672 中的 useRouteNavigate 自定义导航 hook。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Sonnet 4.6 committed 2026-05-14 19:29:35 +08:00
commit 28948975db
77 files changed
+19667 -159

No files matched your search

+12 -1
View File
@@ -2,6 +2,7 @@
import { BotIcon, PlusSquare } from "lucide-react";
import { useParams, useRouter } from "next/navigation";
import { useRouteBase } from "@/core/route-base";
import { useCallback, useEffect, useState } from "react";
import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
@@ -32,6 +33,7 @@ import { env } from "@/env";
import { cn } from "@/lib/utils";
export default function AgentChatPage() {
const routeBase = useRouteBase();
const { t } = useI18n();
const [showFollowups, setShowFollowups] = useState(false);
const [hasStartedConversation, setHasStartedConversation] = useState(false);
@@ -66,7 +68,7 @@ export default function AgentChatPage() {
history.replaceState(
null,
"",
`${window.location.pathname}${window.location.search}#/page/workspace/agents/${agent_id}/chats/${createdThreadId}`,
`${window.location.pathname}${window.location.search}#${routeBase}/agents/${agent_id}/chats/${createdThreadId}`,
);
},
onFinish: (state) => {
@@ -98,6 +100,14 @@ export default function AgentChatPage() {
[sendMessage, threadId, agent_id],
);
const handleClarificationSubmit = useCallback(
(answer: string) => {
setHasStartedConversation(true);
void sendMessage(threadId, { text: answer, files: [] }, { agent_id });
},
[sendMessage, threadId, agent_id],
);
const handleStop = useCallback(async () => {
await thread.stop();
}, [thread]);
@@ -160,6 +170,7 @@ export default function AgentChatPage() {
thread={thread}
paddingBottom={messageListPaddingBottom}
tokenUsageEnabled={tokenUsageEnabled}
onClarificationSubmit={handleClarificationSubmit}
/>
</div>
+12 -1
View File
@@ -27,11 +27,13 @@ import { useModels } from "@/core/models/hooks";
import { useNotification } from "@/core/notification/hooks";
import { useThreadSettings } from "@/core/settings";
import { useThreadStream } from "@/core/threads/hooks";
import { useRouteBase } from "@/core/route-base";
import { textOfMessage } from "@/core/threads/utils";
import { env } from "@/env";
import { cn } from "@/lib/utils";
export default function ChatPage() {
const routeBase = useRouteBase();
const { t } = useI18n();
const [showFollowups, setShowFollowups] = useState(false);
const [hasStartedConversation, setHasStartedConversation] = useState(false);
@@ -68,7 +70,7 @@ export default function ChatPage() {
history.replaceState(
null,
"",
`${window.location.pathname}${window.location.search}#/page/workspace/chats/${createdThreadId}`,
`${window.location.pathname}${window.location.search}#${routeBase}/chats/${createdThreadId}`,
);
},
onFinish: (state) => {
@@ -113,6 +115,14 @@ export default function ChatPage() {
},
[sendMessage, threadId, hasStartedConversation, promptPrefixOn, promptPrefixSettings],
);
const handleClarificationSubmit = useCallback(
(answer: string) => {
setHasStartedConversation(true);
void sendMessage(threadId, { text: answer, files: [] });
},
[sendMessage, threadId],
);
const handleStop = useCallback(async () => {
await thread.stop();
}, [thread]);
@@ -165,6 +175,7 @@ export default function ChatPage() {
thread={thread}
paddingBottom={messageListPaddingBottom}
tokenUsageEnabled={tokenUsageEnabled}
onClarificationSubmit={handleClarificationSubmit}
/>
</div>
<div className="absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4">
+2 -1
View File
@@ -12,10 +12,11 @@ import {
} from "@/components/workspace/workspace-container";
import { useI18n } from "@/core/i18n/hooks";
import { useThreads } from "@/core/threads/hooks";
import { pathOfThread, titleOfThread } from "@/core/threads/utils";
import { usePathOfThread, titleOfThread } from "@/core/threads/utils";
import { formatTimeAgo } from "@/core/utils/datetime";
export default function ChatsPage() {
const pathOfThread = usePathOfThread();
const { t } = useI18n();
const { data: threads } = useThreads();
const [search, setSearch] = useState("");
+24 -17
View File
@@ -4,6 +4,8 @@ import { Navigate, Route, Routes } from "react-router-dom";
import { PageSidebar } from "@/components/page-sidebar";
import WorkspaceRoutes from "./WorkspaceRoutes";
import StrategyRoutes from "./StrategyRoutes";
import Header from "@/strategy-components/Header";
// 设为 true 时隐藏侧边栏(开发中,暂未完成)
const SIDEBAR_ENABLED = false;
@@ -12,31 +14,36 @@ function PageLayout() {
const [collapsed, setCollapsed] = useState(false);
return (
<div className="min-h-screen bg-muted/20">
<div className="h-screen flex bg-muted/20">
{SIDEBAR_ENABLED && (
<PageSidebar
collapsed={collapsed}
onToggleCollapsed={() => setCollapsed((prev) => !prev)}
/>
)}
<main
className={`min-h-screen transition-[padding] duration-300 ${
!SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-16 md:pl-64"
<div
className={`flex flex-col flex-1 overflow-hidden transition-[padding] duration-300 ${
!SIDEBAR_ENABLED ? "" : collapsed ? "pl-16" : "pl-64"
}`}
>
<Routes>
<Route index element={<Navigate to="workspace" replace />} />
<Route
path="workspace/*"
element={
<WorkspaceRoutes
pageSidebarCollapsed={collapsed}
pageSidebarHidden={!SIDEBAR_ENABLED}
/>
}
/>
</Routes>
</main>
{SIDEBAR_ENABLED && <Header />}
<main className="flex-1 overflow-hidden">
<Routes>
<Route index element={<Navigate to="workspace" replace />} />
<Route
path="workspace/*"
element={
<WorkspaceRoutes
pageSidebarCollapsed={collapsed}
pageSidebarHidden={!SIDEBAR_ENABLED}
headerHidden={!SIDEBAR_ENABLED}
/>
}
/>
<Route path="strategy/*" element={<StrategyRoutes />} />
</Routes>
</main>
</div>
</div>
);
}
@@ -2,7 +2,8 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { ChevronDown, Download, Edit3, FileText, ListChecks, Loader2, Save, Trash2, X } from "lucide-react";
import { useNavigate, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useRouteNavigate as useNavigate } from "@/core/route-base";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
@@ -17,7 +17,7 @@ import {
RefreshCw,
Trash2,
} from "lucide-react";
import { useNavigate } from "react-router-dom";
import { useRouteNavigate as useNavigate } from "@/core/route-base";
import { toast } from "sonner";
import { Badge } from "@/components/ui/badge";
+95
View File
@@ -0,0 +1,95 @@
import { Navigate, Route, Routes } from "react-router-dom";
import type { ReactNode } from "react";
import { Toaster } from "sonner";
import { getStoredAuth } from "@/core/auth";
import { RouteBaseProvider } from "@/core/route-base";
import { SubtasksProvider } from "@/core/tasks/context";
import { ArtifactsProvider } from "@/components/workspace/artifacts";
import { PromptInputProvider } from "@/components/ai-elements/prompt-input";
import GeneralQA from "@/strategy-components/components/qa/GeneralQA";
import AgentsPage from "./AgentsPage";
import ChatsPage from "./ChatsPage";
import ChatPage from "./ChatPage";
import NewAgentPage from "./NewAgentPage";
import AgentChatPage from "./AgentChatPage";
import ScheduledTasksPage from "./ScheduledTasksPage";
import ScheduledTaskRunDetailPage from "./ScheduledTaskRunDetailPage";
import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
function ChatRuntime({ children }: { children: ReactNode }) {
return (
<SubtasksProvider>
<ArtifactsProvider>
<PromptInputProvider>{children}</PromptInputProvider>
</ArtifactsProvider>
</SubtasksProvider>
);
}
export default function StrategyRoutes() {
return (
<RouteBaseProvider value="/page/strategy">
<div className="h-full w-full overflow-hidden">
<Routes>
<Route index element={<Navigate to="qa/general" replace />} />
{/* 问答中心 */}
<Route path="qa/general" element={<GeneralQA />} />
{/* 对话 */}
<Route path="chats" element={<ChatsPage />} />
<Route
path="chats/:thread_id"
element={
<ChatRuntime>
<ChatPage />
</ChatRuntime>
}
/>
{/* 智能体管理 */}
<Route path="agents" element={<AgentsPage />} />
<Route path="agents/new" element={<NewAgentPage />} />
<Route
path="agents/:agent_id/chats"
element={<Navigate to="/page/strategy/agents" replace />}
/>
<Route
path="agents/:agent_id/chats/:thread_id"
element={
<ChatRuntime>
<AgentChatPage />
</ChatRuntime>
}
/>
{/* 定时任务 */}
<Route
path="scheduled"
element={<Navigate to="/page/strategy/scheduled/tasks" replace />}
/>
<Route path="scheduled/tasks" element={<ScheduledTasksPage />} />
<Route
path="scheduled/runs/:run_id"
element={<ScheduledTaskRunDetailPage />}
/>
{/* 设置 */}
<Route
path="settings/recommended-questions"
element={
getStoredAuth()?.system_role === "admin" ? (
<RecommendedQuestionsPage />
) : (
<Navigate to="/page/strategy/chats" replace />
)
}
/>
</Routes>
</div>
<Toaster position="top-center" />
</RouteBaseProvider>
);
}
+12 -3
View File
@@ -11,6 +11,8 @@ import { Toaster } from "sonner";
import { SubtasksProvider } from "@/core/tasks/context";
import { ArtifactsProvider } from "@/components/workspace/artifacts";
import { RouteBaseProvider } from "@/core/route-base";
import ChatsPage from "./ChatsPage";
import ChatPage from "./ChatPage";
import AgentsPage from "./AgentsPage";
@@ -22,14 +24,16 @@ import LlmMetricsPage from "./LlmMetricsPage";
import PromptPrefixSettingsPage from "./PromptPrefixSettingsPage";
import RecommendedQuestionsPage from "./RecommendedQuestionsPage";
function WorkspaceLayout({
export function WorkspaceLayout({
children,
pageSidebarCollapsed,
pageSidebarHidden = false,
headerHidden = false,
}: {
children: ReactNode;
pageSidebarCollapsed: boolean;
pageSidebarHidden?: boolean;
headerHidden?: boolean;
}) {
const pageSidebarWidth = pageSidebarHidden
? "0rem"
@@ -39,10 +43,11 @@ function WorkspaceLayout({
return (
<SidebarProvider
className="h-screen w-full overflow-x-hidden [&_[data-slot=sidebar-container]]:left-[var(--page-sidebar-width)]"
className="h-full w-full overflow-x-hidden [&_[data-slot=sidebar-container]]:left-[var(--page-sidebar-width)]"
style={
{
"--page-sidebar-width": pageSidebarWidth,
"--header-height": headerHidden ? "0rem" : "4rem",
} as CSSProperties
}
// Keep the fixed workspace sidebar aligned to the outer page sidebar width.
@@ -71,13 +76,16 @@ function ChatRuntime({ children }: { children: ReactNode }) {
export default function WorkspaceRoutes({
pageSidebarCollapsed = false,
pageSidebarHidden = false,
headerHidden = false,
}: {
pageSidebarCollapsed?: boolean;
pageSidebarHidden?: boolean;
headerHidden?: boolean;
}) {
const layoutProps = { pageSidebarCollapsed, pageSidebarHidden };
const layoutProps = { pageSidebarCollapsed, pageSidebarHidden, headerHidden };
return (
<RouteBaseProvider value="/page/workspace">
<Routes>
<Route index element={<Navigate to="chats/new" replace />} />
<Route
@@ -185,5 +193,6 @@ export default function WorkspaceRoutes({
}
/>
</Routes>
</RouteBaseProvider>
);
}