From 210fa7d3c63ad5bd8be623642b4c1fce1fe41fdc Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 3 Mar 2026 23:34:50 +0000 Subject: [PATCH] feat(chat): add response feedback thumbs up/down (M9.5) Thumbs up/down buttons on assistant messages (hover action bar). Feedback stored per message, persisted in IDB, included in conversation exports. Toggle to clear feedback. Color-coded: green for up, red for down. Co-Authored-By: Claude Opus 4.6 --- .../app/src/components/chat/ChatMessage.vue | 25 +++++++++++++++++++ .../app/src/components/chat/ChatWindow.vue | 7 ++++++ packages/app/src/stores/chat.ts | 11 ++++++++ packages/core/src/types/message.ts | 2 ++ 4 files changed, 45 insertions(+) diff --git a/packages/app/src/components/chat/ChatMessage.vue b/packages/app/src/components/chat/ChatMessage.vue index 6731c729..bd516a10 100644 --- a/packages/app/src/components/chat/ChatMessage.vue +++ b/packages/app/src/components/chat/ChatMessage.vue @@ -43,6 +43,25 @@ > + + +
() +function toggleFeedback(value: 'up' | 'down') { + const newValue = props.message.feedback === value ? undefined : value + emit('feedback', props.message.id, newValue) +} + // Editing state const isEditing = ref(false) const editContent = ref('') diff --git a/packages/app/src/components/chat/ChatWindow.vue b/packages/app/src/components/chat/ChatWindow.vue index 91d0c3dd..62ed97e6 100644 --- a/packages/app/src/components/chat/ChatWindow.vue +++ b/packages/app/src/components/chat/ChatWindow.vue @@ -74,6 +74,7 @@ @regenerate="handleRegenerate" @branch="handleBranch" @reply="handleReply" + @feedback="handleFeedback" />
@@ -249,6 +250,12 @@ function handleBranch(messageId: string) { chatStore.branchFromMessage(convId, messageId) } +function handleFeedback(messageId: string, value: 'up' | 'down' | undefined) { + const convId = chatStore.activeConversationId + if (!convId) return + chatStore.setMessageFeedback(convId, messageId, value) +} + function handleExtract(text: string) { // Run content extraction without sending to AI const convId = chatStore.activeConversationId ?? chatStore.createConversation() diff --git a/packages/app/src/stores/chat.ts b/packages/app/src/stores/chat.ts index bd652524..c8032bb1 100644 --- a/packages/app/src/stores/chat.ts +++ b/packages/app/src/stores/chat.ts @@ -222,6 +222,16 @@ export const useChatStore = defineStore('chat', () => { } } + function setMessageFeedback(conversationId: string, messageId: string, feedback: 'up' | 'down' | undefined) { + const conv = conversations.value.get(conversationId) + if (!conv) return + const msg = conv.messages.find((m: { id: string }) => m.id === messageId) + if (msg) { + msg.feedback = feedback + debouncedIDBSave(conv) + } + } + function switchSide() { panelSide.value = panelSide.value === 'right' ? 'left' : 'right' } @@ -348,6 +358,7 @@ export const useChatStore = defineStore('chat', () => { addMessage, appendToLastMessage, setMessageWebResults, + setMessageFeedback, switchSide, toggleChatCollapse, setActiveConversation, diff --git a/packages/core/src/types/message.ts b/packages/core/src/types/message.ts index 0fc7a8bb..717b2a82 100644 --- a/packages/core/src/types/message.ts +++ b/packages/core/src/types/message.ts @@ -32,6 +32,8 @@ export interface Message { editedAt?: number /** Attached images (base64, max 4) for vision-capable models */ images?: ImageAttachment[] + /** User feedback: 'up' = thumbs up, 'down' = thumbs down */ + feedback?: 'up' | 'down' } export interface Reaction {