diff --git a/.gitignore b/.gitignore index b5a811a5..8b1c74db 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ frontend/imports # ignore the legacy `web` folder web/ memo.md +.codex diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 0017eb01..47e477c2 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -57,6 +57,12 @@ export default function ChatPage() { } = useArtifacts(); const { threadId, isNewThread, setIsNewThread, isMock } = useThreadChat(); const searchParams = useSearchParams(); + // History render rules: + // - /workspace/chats/{thread_id}: always render history + // - /workspace/chats/new: render history only when xclaw_used=true + const shouldRenderHistory = + !isNewThread || + searchParams.get("xclaw_used")?.trim().toLowerCase() === "true"; // Submission strategy: // - xclaw_used=true: follow `isnew` (isnew=false => reuse existing thread) @@ -65,6 +71,13 @@ export default function ChatPage() { if (!isNewThread) { return false; } + const queryThreadId = searchParams.get("thread_id")?.trim(); + const reuseExistingThread = + !!queryThreadId && + searchParams.get("isnew")?.trim().toLowerCase() === "false"; + if (reuseExistingThread) { + return false; + } if (searchParams.get("xclaw_used")?.trim().toLowerCase() !== "true") { return true; } @@ -115,6 +128,22 @@ export default function ChatPage() { const [hasSubmitted, setHasSubmitted] = useState(false); const showInputBox = !(isNewThread && thread.isThreadLoading); + const [historyCutoff, setHistoryCutoff] = useState(null); + + useEffect(() => { + if (shouldRenderHistory) { + setHistoryCutoff(null); + return; + } + if (historyCutoff === null && !thread.isThreadLoading) { + setHistoryCutoff(thread.messages.length); + } + }, [ + historyCutoff, + shouldRenderHistory, + thread.isThreadLoading, + thread.messages.length, + ]); useEffect(() => { const pageTitle = isNewThread @@ -231,7 +260,7 @@ export default function ChatPage() { )}
- + {/* */}
- +
diff --git a/frontend/src/components/ai-elements/artifact.tsx b/frontend/src/components/ai-elements/artifact.tsx index c91ec28b..cfd8f659 100644 --- a/frontend/src/components/ai-elements/artifact.tsx +++ b/frontend/src/components/ai-elements/artifact.tsx @@ -142,6 +142,6 @@ export const ArtifactContent = ({ }: ArtifactContentProps) => (
{/*
*/} -
+
); diff --git a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx index fa96d33b..fb24baa3 100644 --- a/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx +++ b/frontend/src/components/workspace/artifacts/artifact-file-detail.tsx @@ -527,11 +527,11 @@ export function ArtifactFilePreview({ if (language === "markdown") { return (
diff --git a/frontend/src/components/workspace/chats/use-thread-chat.ts b/frontend/src/components/workspace/chats/use-thread-chat.ts index 1b848f23..e86a310b 100644 --- a/frontend/src/components/workspace/chats/use-thread-chat.ts +++ b/frontend/src/components/workspace/chats/use-thread-chat.ts @@ -3,22 +3,22 @@ import { useParams, usePathname, useSearchParams } from "next/navigation"; import { useEffect, useState } from "react"; -import { uuid } from "@/core/utils/uuid"; - export function useThreadChat() { const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>(); const pathname = usePathname(); const searchParams = useSearchParams(); const xClawUsedFromQuery = searchParams.get("xclaw_used"); + const isNewFromQuery = + searchParams.get("isnew")?.trim().toLowerCase() === "false"; + const queryThreadIdFromParams = searchParams.get("thread_id")?.trim(); + const shouldUseQueryThreadId = + pathname.startsWith("/workspace/chats/") && + !!queryThreadIdFromParams && + (xClawUsedFromQuery === "true" || isNewFromQuery); const [threadId, setThreadId] = useState(() => { if (threadIdFromPath === "new") { - const shouldUseQueryThreadId = pathname.startsWith("/workspace/chats/"); - const queryThreadId = - shouldUseQueryThreadId && xClawUsedFromQuery === "true" - ? searchParams.get("thread_id")?.trim() - : undefined; - return queryThreadId ?? uuid(); + return shouldUseQueryThreadId ? queryThreadIdFromParams : undefined; } return threadIdFromPath; }); @@ -30,12 +30,15 @@ export function useThreadChat() { useEffect(() => { if (pathname.endsWith("/new")) { setIsNewThread(true); - const shouldUseQueryThreadId = pathname.startsWith("/workspace/chats/"); - const queryThreadId = - shouldUseQueryThreadId && xClawUsedFromQuery === "true" - ? searchParams.get("thread_id")?.trim() - : undefined; - setThreadId(queryThreadId ?? uuid()); + const nextQueryThreadId = searchParams.get("thread_id")?.trim(); + const nextIsNewFromQuery = + searchParams.get("isnew")?.trim().toLowerCase() === "false"; + const nextXClawUsed = searchParams.get("xclaw_used"); + const nextShouldUseQueryThreadId = + pathname.startsWith("/workspace/chats/") && + !!nextQueryThreadId && + (nextXClawUsed === "true" || nextIsNewFromQuery); + setThreadId(nextShouldUseQueryThreadId ? nextQueryThreadId : undefined); return; } setIsNewThread(false); diff --git a/frontend/src/hooks/use-iframe-skill.ts b/frontend/src/hooks/use-iframe-skill.ts index 419b19fc..f33bdabb 100644 --- a/frontend/src/hooks/use-iframe-skill.ts +++ b/frontend/src/hooks/use-iframe-skill.ts @@ -42,7 +42,6 @@ export function useIframeSkill(): UseIframeSkillReturn { // 0. 监听 query 中 XClawUsed=true 且带 thread_id 时跳转并清理 query useEffect(() => { - console.log(xClawUsedFromQuery, threadIdFromQuery, lastThreadIdRef.current); if (!threadIdFromQuery) return; if (xClawUsedFromQuery !== "true") return;