From 8f7eb28c0d538d8db8cb5e8d71224b8ff44470a2 Mon Sep 17 00:00:00 2001 From: rayhpeng Date: Sun, 12 Apr 2026 09:43:36 +0800 Subject: [PATCH] fix(rebase): restore FeedbackButtons component and enrichment lost during rebase The FeedbackButtons component (defined inline in message-list-item.tsx) was introduced in commit 95df8d13 but lost during rebase. The previous rebase cleanup commit incorrectly removed the feedback/runId props and enrichment hook as "orphaned code" instead of restoring the missing component. This commit restores: - FeedbackButtons component with thumbs up/down toggle and optimistic state - FeedbackData/upsertFeedback/deleteFeedback imports - feedback and runId props on MessageListItem - useThreadMessageEnrichment hook and entry lookup in message-list.tsx Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workspace/messages/message-list-item.tsx | 85 ++++++++++++++++++- .../workspace/messages/message-list.tsx | 5 ++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/workspace/messages/message-list-item.tsx b/frontend/src/components/workspace/messages/message-list-item.tsx index c9583f7a5..faad2373e 100644 --- a/frontend/src/components/workspace/messages/message-list-item.tsx +++ b/frontend/src/components/workspace/messages/message-list-item.tsx @@ -1,6 +1,6 @@ import type { Message } from "@langchain/langgraph-sdk"; -import { FileIcon, Loader2Icon } from "lucide-react"; -import { memo, useMemo, type ImgHTMLAttributes } from "react"; +import { FileIcon, Loader2Icon, ThumbsDownIcon, ThumbsUpIcon } from "lucide-react"; +import { memo, useCallback, useMemo, useState, type ImgHTMLAttributes } from "react"; import rehypeKatex from "rehype-katex"; import { Loader } from "@/components/ai-elements/loader"; @@ -17,6 +17,11 @@ import { } from "@/components/ai-elements/reasoning"; import { Task, TaskTrigger } from "@/components/ai-elements/task"; import { Badge } from "@/components/ui/badge"; +import { + deleteFeedback, + upsertFeedback, + type FeedbackData, +} from "@/core/api/feedback"; import { resolveArtifactURL } from "@/core/artifacts/utils"; import { useI18n } from "@/core/i18n/hooks"; import { @@ -34,16 +39,85 @@ import { CopyButton } from "../copy-button"; import { MarkdownContent } from "./markdown-content"; +function FeedbackButtons({ + threadId, + runId, + initialFeedback, +}: { + threadId: string; + runId: string; + initialFeedback: FeedbackData | null; +}) { + const [feedback, setFeedback] = useState(initialFeedback); + const [isSubmitting, setIsSubmitting] = useState(false); + + const handleClick = useCallback( + async (rating: number) => { + if (isSubmitting) return; + setIsSubmitting(true); + try { + if (feedback?.rating === rating) { + await deleteFeedback(threadId, runId); + setFeedback(null); + } else { + const result = await upsertFeedback(threadId, runId, rating); + setFeedback(result); + } + } catch { + // Revert on error — feedback state unchanged on catch + } finally { + setIsSubmitting(false); + } + }, + [threadId, runId, feedback, isSubmitting], + ); + + return ( +
+ + +
+ ); +} + export function MessageListItem({ className, threadId, message, isLoading, + feedback, + runId, }: { className?: string; threadId: string; message: Message; isLoading?: boolean; + feedback?: FeedbackData | null; + runId?: string; }) { const isHuman = message.type === "human"; return ( @@ -72,6 +146,13 @@ export function MessageListItem({ "" } /> + {feedback !== undefined && runId && threadId && ( + + )} )} diff --git a/frontend/src/components/workspace/messages/message-list.tsx b/frontend/src/components/workspace/messages/message-list.tsx index cd167f8e4..b02ec2716 100644 --- a/frontend/src/components/workspace/messages/message-list.tsx +++ b/frontend/src/components/workspace/messages/message-list.tsx @@ -18,6 +18,7 @@ import { useRehypeSplitWordsIntoSpans } from "@/core/rehype"; import type { Subtask } from "@/core/tasks"; import { useUpdateSubtask } from "@/core/tasks/context"; import type { AgentThreadState } from "@/core/threads"; +import { useThreadMessageEnrichment } from "@/core/threads/hooks"; import { cn } from "@/lib/utils"; import { ArtifactFileList } from "../artifacts/artifact-file-list"; @@ -47,6 +48,7 @@ export function MessageList({ const rehypePlugins = useRehypeSplitWordsIntoSpans(thread.isLoading); const updateSubtask = useUpdateSubtask(); const messages = thread.messages; + const { data: enrichment } = useThreadMessageEnrichment(threadId); if (thread.isThreadLoading && messages.length === 0) { return ; @@ -59,12 +61,15 @@ export function MessageList({ {groupMessages(messages, (group) => { if (group.type === "human" || group.type === "assistant") { return group.messages.map((msg) => { + const entry = msg.id ? enrichment?.get(msg.id) : undefined; return ( ); });