✨ custom feedback highlights
This commit is contained in:
@@ -7,8 +7,6 @@ import { parseAnnotationsFromAIResponse } from '../../utils/annotationParser'
|
||||
import type { Attachment, TextAnnotation } from '../../types/editor'
|
||||
import './Chat.css'
|
||||
|
||||
type TabId = 'chat' | 'feedback'
|
||||
|
||||
function formatSessionTime(createdAt: number): string {
|
||||
const date = new Date(createdAt)
|
||||
const now = new Date()
|
||||
@@ -41,10 +39,13 @@ export function ChatPanel(): JSX.Element {
|
||||
setAnnotations,
|
||||
linkAnnotationsToMessage,
|
||||
newChat,
|
||||
setActiveSession
|
||||
setActiveSession,
|
||||
rightPanelTab,
|
||||
setRightPanelTab,
|
||||
} = useEditorStore()
|
||||
|
||||
const [tab, setTab] = useState<TabId>('chat')
|
||||
const tab = rightPanelTab
|
||||
const setTab = setRightPanelTab
|
||||
const [showHistory, setShowHistory] = useState(false)
|
||||
const [pendingAttachmentCount, setPendingAttachmentCount] = useState(0)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -116,7 +116,9 @@ export function analyseAnnotation(
|
||||
documentContent: activeFileContent,
|
||||
documentPath: activeFilePath ?? '',
|
||||
conversationHistory: [],
|
||||
userMessage: `This passage was flagged for ${typeName}: "${ann.matchedText}"\n\nIn 1–2 sentences explain the specific issue, then provide a direct rewrite in a markdown blockquote like this:\n\n> Rewritten passage here.\n\nBe specific to this exact text—no generic advice.`
|
||||
userMessage: ann.autoAnalyse
|
||||
? `Please provide editorial feedback on this selected passage: "${ann.matchedText}"\n\nIn 1–2 sentences, identify the most important issue or opportunity for improvement, then provide a suggested rewrite in a markdown blockquote:\n\n> Rewritten version here.\n\nBe specific to this exact text.`
|
||||
: `This passage was flagged for ${typeName}: "${ann.matchedText}"\n\nIn 1–2 sentences explain the specific issue, then provide a direct rewrite in a markdown blockquote like this:\n\n> Rewritten passage here.\n\nBe specific to this exact text—no generic advice.`
|
||||
},
|
||||
(chunk: string) => {
|
||||
if (cancelled) return
|
||||
@@ -607,7 +609,32 @@ export function MarkdownEditor(): JSX.Element {
|
||||
selectAll(view)
|
||||
view.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
...(hasSelection ? [
|
||||
'separator' as const,
|
||||
{
|
||||
label: 'Generate feedback',
|
||||
action: () => {
|
||||
const view = viewRef.current
|
||||
if (!view) return
|
||||
const { from, to } = view.state.selection.main
|
||||
const text = view.state.sliceDoc(from, to)
|
||||
if (!text.trim()) return
|
||||
const annotation: TextAnnotation = {
|
||||
id: `custom-${Date.now()}`,
|
||||
type: 'custom',
|
||||
from,
|
||||
to,
|
||||
matchedText: text,
|
||||
message: 'Generating feedback...',
|
||||
autoAnalyse: true,
|
||||
}
|
||||
const store = useEditorStore.getState()
|
||||
store.setAnnotations([...store.annotations, annotation])
|
||||
store.setRightPanelTab('feedback')
|
||||
}
|
||||
}
|
||||
] : [])
|
||||
]
|
||||
|
||||
return (
|
||||
|
||||
@@ -44,7 +44,8 @@ function FeedbackCard({ ann, autoAnalyse, onDismiss }: FeedbackCardProps): JSX.E
|
||||
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') {
|
||||
// Context-menu annotations (autoAnalyse: true) need AI analysis, so start idle.
|
||||
if (ann.type === 'custom' && !ann.autoAnalyse) {
|
||||
return { status: 'done', text: ann.message, suggestion: ann.suggestion ?? null }
|
||||
}
|
||||
return { status: 'idle' }
|
||||
@@ -93,7 +94,7 @@ function FeedbackCard({ ann, autoAnalyse, onDismiss }: FeedbackCardProps): JSX.E
|
||||
<div className="fb-card-header-top">
|
||||
<div className="fb-card-header-badges">
|
||||
<span className="fb-card-badge">{typeName}</span>
|
||||
{ann.type === 'custom' && (
|
||||
{ann.type === 'custom' && !ann.autoAnalyse && (
|
||||
<span className="fb-card-source-tag">from attachment</span>
|
||||
)}
|
||||
</div>
|
||||
@@ -251,7 +252,7 @@ export function FeedbackPanel(): JSX.Element {
|
||||
<FeedbackCard
|
||||
key={ann.id}
|
||||
ann={ann}
|
||||
autoAnalyse={analyseAll}
|
||||
autoAnalyse={analyseAll || ann.autoAnalyse === true}
|
||||
onDismiss={() => { cancelPendingDismiss(ann.id); removeAnnotation(ann.id); tooltipAnalysisCache.delete(ann.id) }}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -76,6 +76,10 @@ interface EditorState {
|
||||
revisions: RevisionMeta[]
|
||||
setRevisions: (revisions: RevisionMeta[]) => void
|
||||
|
||||
// Right panel tab (chat or feedback) — shared so MarkdownEditor can switch it
|
||||
rightPanelTab: 'chat' | 'feedback'
|
||||
setRightPanelTab: (tab: 'chat' | 'feedback') => void
|
||||
|
||||
// Session persistence
|
||||
loadSession: () => Promise<void>
|
||||
}
|
||||
@@ -483,6 +487,9 @@ export const useEditorStore = create<EditorState>((set, get) => ({
|
||||
|
||||
revisionPanelOpen: false,
|
||||
toggleRevisionPanel: () => set((s) => ({ revisionPanelOpen: !s.revisionPanelOpen })),
|
||||
|
||||
rightPanelTab: 'chat',
|
||||
setRightPanelTab: (tab) => set({ rightPanelTab: tab }),
|
||||
revisions: [],
|
||||
setRevisions: (revisions) => set({ revisions }),
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface TextAnnotation {
|
||||
suggestion?: string
|
||||
applied?: boolean // true when the suggestion has been applied to the document
|
||||
dismissed?: boolean // true when the user dismissed this annotation (archived)
|
||||
autoAnalyse?: boolean // true when created via context menu — FeedbackCard starts AI analysis immediately
|
||||
}
|
||||
|
||||
export type AnalysisMode = 'none' | 'passive_voice' | 'consistency' | 'style' | 'critique'
|
||||
|
||||
Reference in New Issue
Block a user