fix: clear Clawd input on send

This commit is contained in:
134 2026-06-23 22:01:03 +08:00
parent 43e01e7613
commit d434485987
3 changed files with 77 additions and 78 deletions

View File

@ -3,6 +3,7 @@ import { z } from "zod";
import { auth } from "@/app/(auth)/auth";
import {
getChatById,
getMessagesByChatId,
getSystemSetting,
saveChat,
saveMessages,
@ -91,7 +92,6 @@ export async function POST(request: Request) {
const userMessageId = parsed.data.userMessageId || generateUUID();
const assistantMessageId = generateUUID();
const now = new Date();
const conversationId = createConversationId(session.user.id, parsed.data.id);
const endpoint = `${config.baseUrl}/api/chat`;
@ -159,13 +159,28 @@ export async function POST(request: Request) {
);
}
const existingMessages = await getMessagesByChatId({ id: parsed.data.id }).catch(
() => []
);
const latestMessageTimestamp = existingMessages.reduce((latest, item) => {
const timestamp =
item.createdAt instanceof Date
? item.createdAt.getTime()
: Date.parse(String(item.createdAt || ""));
return Number.isFinite(timestamp) ? Math.max(latest, timestamp) : latest;
}, 0);
const userCreatedAt = new Date(
Math.max(Date.now(), latestMessageTimestamp + 1)
);
const assistantCreatedAt = new Date(userCreatedAt.getTime() + 1);
const userMessage = {
id: userMessageId,
chatId: parsed.data.id,
role: "user",
parts: [{ type: "text", text: parsed.data.message }],
attachments: [],
createdAt: now,
createdAt: userCreatedAt,
videoStorageStatus: null,
temporaryVideoUrl: null,
permanentVideoUrl: null,
@ -178,7 +193,7 @@ export async function POST(request: Request) {
role: "assistant",
parts: [{ type: "text", text: answer }],
attachments: [],
createdAt: new Date(now.getTime() + 1),
createdAt: assistantCreatedAt,
videoStorageStatus: null,
temporaryVideoUrl: null,
permanentVideoUrl: null,
@ -199,12 +214,14 @@ export async function POST(request: Request) {
id: userMessage.id,
role: "user",
parts: userMessage.parts,
createdAt: userMessage.createdAt.toISOString(),
metadata: { createdAt: userMessage.createdAt.toISOString() },
},
{
id: assistantMessage.id,
role: "assistant",
parts: assistantMessage.parts,
createdAt: assistantMessage.createdAt.toISOString(),
metadata: { createdAt: assistantMessage.createdAt.toISOString() },
},
],

View File

@ -3367,6 +3367,23 @@ export function CanvasChatPanel({
: isPresetAgentSelection
? getToolbarItem("chatModePresetAgent").label
: "Agent";
const getComposerModeMenuItemClass = useCallback(
(mode: CanvasComposerMode) =>
cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal outline-none transition-colors focus:text-[#1f2937] dark:focus:text-white [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground focus:bg-background/80"
: "text-[#2e3238] focus:bg-[#eef0f3] dark:text-zinc-100 dark:focus:bg-white/12",
activeComposerMode === mode
? isVideoWorkspace
? "bg-background text-foreground"
: "bg-[#eef0f3] text-[#1f2937] dark:bg-white/12 dark:text-white"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
),
[activeComposerMode, isVideoWorkspace]
);
const shouldMaskAgentRuntimeModelName =
activeComposerMode === "agent" || selectedModelId === "auto";
const getAgentSafeModelName = useCallback(
@ -8536,11 +8553,19 @@ export function CanvasChatPanel({
}
const userMessageId = generateUUID();
const latestMessageTimestamp = messages.reduce(
(latest, message) => Math.max(latest, getMessageTimestamp(message) ?? 0),
0
);
const optimisticCreatedAt = new Date(
Math.max(Date.now(), latestMessageTimestamp + 1)
);
const optimisticUserMessage: ChatMessage = {
id: userMessageId,
role: "user",
parts: [{ type: "text", text: textContent }],
metadata: { createdAt: new Date().toISOString() },
createdAt: optimisticCreatedAt,
metadata: { createdAt: optimisticCreatedAt.toISOString() },
} as ChatMessage;
setSendGuard(activeChatId, {
@ -8555,6 +8580,14 @@ export function CanvasChatPanel({
optimisticUserMessage,
])
);
setInput("");
setAttachments([]);
clearSelection();
const editableDiv = textareaRef.current as HTMLDivElement | null;
if (editableDiv) {
editableDiv.innerHTML = "";
}
try {
const response = await fetch("/api/canvas/clawd-chat", {
@ -8594,17 +8627,19 @@ export function CanvasChatPanel({
void mutate(buildHistoryMessagesUrl(rawChatId, { cacheBust: true }));
}
hasHydratedHistoryRef.current = true;
setInput("");
setAttachments([]);
clearSelection();
const editableDiv = textareaRef.current as HTMLDivElement | null;
if (editableDiv) {
editableDiv.innerHTML = "";
}
} catch (error) {
console.error("[CanvasChat] Clawd request failed:", error);
toast.error(error instanceof Error ? error.message : "Clawd 请求失败");
const currentEditorText = (editableDiv?.textContent || "")
.replace(/\u200b/g, " ")
.replace(/\s+/g, " ")
.trim();
if (!currentEditorText) {
setInput(textContent);
if (editableDiv) {
editableDiv.textContent = textContent;
}
}
} finally {
setIsClawdSubmitting(false);
releaseSendGuard(activeChatId);
@ -12448,7 +12483,7 @@ export function CanvasChatPanel({
side="top"
sideOffset={10}
className={cn(
"w-[200px] rounded-[18px] border border-[#d8dce3] bg-white p-2 text-[#20242a] shadow-[0_22px_54px_rgba(15,23,42,0.16)]",
"w-[200px] space-y-1 rounded-[18px] border border-[#d8dce3] bg-white p-2 text-[#20242a] shadow-[0_22px_54px_rgba(15,23,42,0.16)]",
isVideoWorkspace
? "border border-border/60 bg-background/95 shadow-[0_14px_30px_-20px_rgba(15,23,42,0.35)] backdrop-blur-xl"
: "dark:border-white/10 dark:bg-zinc-900/95 dark:text-zinc-100"
@ -12457,19 +12492,7 @@ export function CanvasChatPanel({
{effectiveAgentEnabled ? (
<DropdownMenuItem
onClick={() => handleSelectGenerationMode("agent")}
className={cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground"
: "text-[#2e3238] dark:text-zinc-100",
activeComposerMode === "agent"
? isVideoWorkspace
? "bg-background"
: "bg-transparent dark:bg-white/10"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
)}
className={getComposerModeMenuItemClass("agent")}
>
<CanvasToolbarIcon
className="h-5 w-5"
@ -12487,19 +12510,7 @@ export function CanvasChatPanel({
{isClawdModeEnabled ? (
<DropdownMenuItem
onClick={() => handleSelectGenerationMode("clawd")}
className={cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground"
: "text-[#2e3238] dark:text-zinc-100",
activeComposerMode === "clawd"
? isVideoWorkspace
? "bg-background"
: "bg-transparent dark:bg-white/10"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
)}
className={getComposerModeMenuItemClass("clawd")}
>
<MessageCircle className="h-5 w-5" />
<span className="flex-1">Clawd模式</span>
@ -12511,19 +12522,7 @@ export function CanvasChatPanel({
{canUseDetailGeneration ? (
<DropdownMenuItem
onClick={() => handleSelectGenerationMode("detail")}
className={cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground"
: "text-[#2e3238] dark:text-zinc-100",
activeComposerMode === "detail"
? isVideoWorkspace
? "bg-background"
: "bg-transparent dark:bg-white/10"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
)}
className={getComposerModeMenuItemClass("detail")}
>
<CanvasToolbarIcon
className="h-5 w-5"
@ -12540,19 +12539,7 @@ export function CanvasChatPanel({
) : null}
<DropdownMenuItem
onClick={() => handleSelectGenerationMode("image")}
className={cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground"
: "text-[#2e3238] dark:text-zinc-100",
activeComposerMode === "image"
? isVideoWorkspace
? "bg-background"
: "bg-transparent dark:bg-white/10"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
)}
className={getComposerModeMenuItemClass("image")}
>
<CanvasToolbarIcon
className="h-5 w-5"
@ -12568,19 +12555,7 @@ export function CanvasChatPanel({
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleSelectGenerationMode("video")}
className={cn(
"h-11 cursor-pointer rounded-[12px] px-3 text-[16px] font-normal [&_svg]:!size-5",
isVideoWorkspace
? "text-foreground"
: "text-[#2e3238] dark:text-zinc-100",
activeComposerMode === "video"
? isVideoWorkspace
? "bg-background"
: "bg-transparent dark:bg-white/10"
: isVideoWorkspace
? "hover:bg-background/80"
: "hover:bg-[#f5f6f7] dark:hover:bg-white/8"
)}
className={getComposerModeMenuItemClass("video")}
>
<CanvasToolbarIcon
className="h-5 w-5"

View File

@ -328,10 +328,17 @@ export function messageHasAssistantMedia(
}
export function getMessageTimestamp(message: ChatMessage): number | null {
const metadata =
(message as any).metadata && typeof (message as any).metadata === "object"
? ((message as any).metadata as Record<string, unknown>)
: null;
const rawValue =
(message as any).createdAt ??
(message as any).created_at ??
(message as any).timestamp ??
metadata?.createdAt ??
metadata?.created_at ??
metadata?.timestamp ??
null;
if (rawValue instanceof Date) {