diff --git a/src/main/aiService.ts b/src/main/aiService.ts index 2ca22f0..34c742e 100644 --- a/src/main/aiService.ts +++ b/src/main/aiService.ts @@ -93,12 +93,23 @@ Name the single most important thing to fix in a revision of this chapter.` if (payload.attachments && payload.attachments.length > 0) { const names = payload.attachments.map((a) => a.name).join(', ') - prompt += `\n\nThe user has attached the following reference file(s): ${names}. -When suggesting edits based on the attached material, identify exact passages in the chapter and use this format for each suggestion: -ISSUE: [category of improvement] -PASSAGE: "[exact quoted text from the chapter]" -PROBLEM: [brief explanation of why this needs changing] -SUGGESTION: "[revised text]"` + prompt += `\n\n--- +ATTACHED REFERENCE MATERIAL: ${names} + +The user has provided reference file(s) above. Based on these references and the user's instruction, identify specific passages in the chapter that should be changed. + +You MUST output every suggested edit in this exact structured format — no other format will create highlights in the editor: + +ISSUE: [short category label, e.g. "Tone", "Vocabulary", "Style Match", "Pacing"] +PASSAGE: "[copy the exact verbatim text from the chapter — at least 10 characters, unique enough to be found]" +PROBLEM: [one sentence explaining why this passage needs changing in light of the reference material] +SUGGESTION: "[the revised replacement text]" + +Rules: +- PASSAGE must be a verbatim copy of text that exists in the chapter above. Do not paraphrase. +- Include as many ISSUE/PASSAGE/PROBLEM/SUGGESTION blocks as are genuinely warranted. +- After all structured blocks, you may add a brief overall summary. +---` } return prompt diff --git a/src/renderer/components/AIChat/ChatPanel.tsx b/src/renderer/components/AIChat/ChatPanel.tsx index f5ed241..d93cbfc 100644 --- a/src/renderer/components/AIChat/ChatPanel.tsx +++ b/src/renderer/components/AIChat/ChatPanel.tsx @@ -68,11 +68,14 @@ export function ChatPanel(): JSX.Element { } ) - // After streaming, parse AI response for annotations + // After streaming, parse AI response for annotations. + // Attachment-driven messages are tagged 'custom' so they appear with a + // distinct visual treatment in the Feedback panel. const currentHistory = useEditorStore.getState().chatHistory const lastMsg = currentHistory[currentHistory.length - 1] if (lastMsg?.role === 'assistant' && lastMsg.content.length > 0) { - const parsed = parseAnnotationsFromAIResponse(lastMsg.content, activeFileContent) + const overrideType = attachments.length > 0 ? 'custom' as const : undefined + const parsed = parseAnnotationsFromAIResponse(lastMsg.content, activeFileContent, overrideType) if (parsed.length > 0) { setAnnotations(parsed) } diff --git a/src/renderer/components/Feedback/FeedbackPanel.css b/src/renderer/components/Feedback/FeedbackPanel.css index 9fce220..81e3750 100644 --- a/src/renderer/components/Feedback/FeedbackPanel.css +++ b/src/renderer/components/Feedback/FeedbackPanel.css @@ -123,6 +123,14 @@ color: var(--text-primary); } +/* Wrapper that holds the type badge + optional source tag side by side */ +.fb-card-header-badges { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + /* Coloured type badge */ .fb-card-badge { font-size: 10px; @@ -137,6 +145,20 @@ align-self: flex-start; } +/* Small label shown on attachment-driven (custom) annotations */ +.fb-card-source-tag { + font-size: 9px; + font-weight: 600; + letter-spacing: 0.06em; + text-transform: uppercase; + color: rgba(30, 200, 150, 0.75); + border: 1px solid rgba(30, 200, 150, 0.3); + border-radius: 3px; + padding: 1px 5px; + align-self: flex-start; + white-space: nowrap; +} + /* Passage excerpt — wraps so the full text is always visible */ .fb-card-excerpt { font-size: 12px; diff --git a/src/renderer/components/Feedback/FeedbackPanel.tsx b/src/renderer/components/Feedback/FeedbackPanel.tsx index 4d96114..723328c 100644 --- a/src/renderer/components/Feedback/FeedbackPanel.tsx +++ b/src/renderer/components/Feedback/FeedbackPanel.tsx @@ -23,6 +23,7 @@ function badgeColor(type: TextAnnotation['type']): string { case 'consistency': return 'rgba(220, 80, 80, 0.75)' case 'style': return 'rgba(80, 160, 255, 0.75)' case 'critique': return 'rgba(160, 80, 220, 0.75)' + case 'custom': return 'rgba(30, 200, 150, 0.8)' } } @@ -41,6 +42,11 @@ function FeedbackCard({ ann, autoAnalyse, onDismiss }: FeedbackCardProps): JSX.E const [state, setState] = useState(() => { const cached = tooltipAnalysisCache.get(ann.id) if (cached) return { status: 'done', text: cached.text, suggestion: cached.suggestion } + // Custom (attachment-driven) annotations already carry their analysis — show + // the problem description and suggestion immediately without an extra AI call. + if (ann.type === 'custom') { + return { status: 'done', text: ann.message, suggestion: ann.suggestion ?? null } + } return { status: 'idle' } }) @@ -85,7 +91,12 @@ function FeedbackCard({ ann, autoAnalyse, onDismiss }: FeedbackCardProps): JSX.E {/* Header — click to jump to passage in editor */}
scrollToAnnotation(ann)} title="Jump to passage">
- {typeName} +
+ {typeName} + {ann.type === 'custom' && ( + from attachment + )} +