From 84377c82afb8ee20cf66b435913a9aca327c0483 Mon Sep 17 00:00:00 2001 From: Alex Hernandez Date: Mon, 2 Mar 2026 18:57:24 +1000 Subject: [PATCH] :lightning: smarter checking for custom feedback --- src/renderer/components/AIChat/Chat.css | 9 +++++++++ src/renderer/components/AIChat/ChatPanel.tsx | 13 ++++++++++++- src/renderer/components/Toolbar/AnalysisToolbar.tsx | 2 +- src/renderer/utils/annotationParser.ts | 10 +++++++--- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/renderer/components/AIChat/Chat.css b/src/renderer/components/AIChat/Chat.css index 7069456..53db585 100644 --- a/src/renderer/components/AIChat/Chat.css +++ b/src/renderer/components/AIChat/Chat.css @@ -381,6 +381,15 @@ padding: 10px 12px; } +.chat-info-notice { + background: rgba(180, 140, 60, 0.12); + border: 1px solid rgba(180, 140, 60, 0.35); + border-radius: 6px; + color: #c9a94a; + font-size: 12px; + padding: 10px 12px; +} + .chat-input-area { display: flex; flex-direction: column; diff --git a/src/renderer/components/AIChat/ChatPanel.tsx b/src/renderer/components/AIChat/ChatPanel.tsx index b561717..c382dd8 100644 --- a/src/renderer/components/AIChat/ChatPanel.tsx +++ b/src/renderer/components/AIChat/ChatPanel.tsx @@ -48,6 +48,7 @@ export function ChatPanel(): JSX.Element { const setTab = setRightPanelTab const [showHistory, setShowHistory] = useState(false) const [pendingAttachmentCount, setPendingAttachmentCount] = useState(0) + const [feedbackNotice, setFeedbackNotice] = useState(null) const scrollRef = useRef(null) const prevAnnotationCountRef = useRef(annotations.length) @@ -68,6 +69,7 @@ export function ChatPanel(): JSX.Element { if (!activeFilePath || isAILoading) return setAIError(null) + setFeedbackNotice(null) addUserMessage(text, attachments.map(({ name, mimeType }) => ({ name, mimeType }))) startAssistantMessage() setAILoading(true) @@ -97,11 +99,17 @@ export function ChatPanel(): JSX.Element { const lastMsg = currentHistory[currentHistory.length - 1] if (lastMsg?.role === 'assistant' && lastMsg.content.length > 0) { const overrideType = attachments.length > 0 ? 'custom' as const : undefined - const parsed = parseAnnotationsFromAIResponse(lastMsg.content, activeFileContent, overrideType) + const latestContent = useEditorStore.getState().activeFileContent + const { annotations: parsed, droppedCount } = parseAnnotationsFromAIResponse(lastMsg.content, latestContent, overrideType) if (parsed.length > 0) { setAnnotations(parsed) linkAnnotationsToMessage(lastMsg.id, parsed.map(a => a.id)) } + if (droppedCount > 0) { + setFeedbackNotice( + `${droppedCount} feedback item${droppedCount === 1 ? '' : 's'} couldn't be applied — the referenced text has been edited.` + ) + } } } catch (err) { const message = err instanceof Error ? err.message : 'An error occurred' @@ -234,6 +242,9 @@ export function ChatPanel(): JSX.Element { {aiError && (
{aiError}
)} + {feedbackNotice && ( +
{feedbackNotice}
+ )} 0) { const existing = useEditorStore.getState().annotations.filter((a) => a.type !== mode) setAnnotations([...existing, ...newAnnotations]) diff --git a/src/renderer/utils/annotationParser.ts b/src/renderer/utils/annotationParser.ts index 8e0c745..7469f8b 100644 --- a/src/renderer/utils/annotationParser.ts +++ b/src/renderer/utils/annotationParser.ts @@ -7,10 +7,11 @@ export function parseAnnotationsFromAIResponse( aiResponse: string, documentContent: string, overrideType?: AnnotationType -): TextAnnotation[] { +): { annotations: TextAnnotation[]; droppedCount: number } { const annotations: TextAnnotation[] = [] let id = 0 const runId = Date.now() + let droppedCount = 0 // Match quoted strings — handles "straight", "curly", and 'single' quotes // Minimum 10 chars to avoid matching short words @@ -29,7 +30,10 @@ export function parseAnnotationsFromAIResponse( docIndex = findNormalized(documentContent, normalized) } - if (docIndex === -1) continue + if (docIndex === -1) { + droppedCount++ + continue + } // Don't annotate the same range twice const alreadyAnnotated = annotations.some( @@ -61,7 +65,7 @@ export function parseAnnotationsFromAIResponse( }) } - return deduplicateOverlapping(annotations) + return { annotations: deduplicateOverlapping(annotations), droppedCount } } function deduplicateOverlapping(annotations: TextAnnotation[]): TextAnnotation[] {