diff --git a/bun.lock b/bun.lock index b3aa583..abad462 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "plannotator", @@ -53,7 +54,7 @@ }, "apps/opencode-plugin": { "name": "@plannotator/opencode", - "version": "0.9.2", + "version": "0.9.3", "dependencies": { "@opencode-ai/plugin": "^1.1.10", }, @@ -74,7 +75,7 @@ }, "apps/pi-extension": { "name": "@plannotator/pi-extension", - "version": "0.9.2", + "version": "0.9.3", "peerDependencies": { "@mariozechner/pi-coding-agent": ">=0.53.0", }, @@ -134,6 +135,7 @@ "dependencies": { "@pierre/diffs": "^1.0.4", "@plannotator/ui": "workspace:*", + "highlight.js": "^11.11.1", "react": "^19.2.3", "react-dom": "^19.2.3", "tailwindcss": "^4.1.18", @@ -141,7 +143,7 @@ }, "packages/server": { "name": "@plannotator/server", - "version": "0.9.2", + "version": "0.9.3", "dependencies": { "@plannotator/shared": "workspace:*", }, diff --git a/packages/review-editor/App.tsx b/packages/review-editor/App.tsx index 326df76..79fbb91 100644 --- a/packages/review-editor/App.tsx +++ b/packages/review-editor/App.tsx @@ -8,7 +8,7 @@ import { storage } from '@plannotator/ui/utils/storage'; import { CompletionOverlay } from '@plannotator/ui/components/CompletionOverlay'; import { getIdentity } from '@plannotator/ui/utils/identity'; import { getAgentSwitchSettings, getEffectiveAgentName } from '@plannotator/ui/utils/agentSwitch'; -import { CodeAnnotation, CodeAnnotationType, SelectedLineRange, DiffAnnotationMetadata } from '@plannotator/ui/types'; +import { CodeAnnotation, CodeAnnotationType, SelectedLineRange } from '@plannotator/ui/types'; import { useResizablePanel } from '@plannotator/ui/hooks/useResizablePanel'; import { ResizeHandle } from '@plannotator/ui/components/ResizeHandle'; import { DiffViewer } from './components/DiffViewer'; @@ -255,7 +255,8 @@ const ReviewApp: React.FC = () => { const handleAddAnnotation = useCallback(( type: CodeAnnotationType, text?: string, - suggestedCode?: string + suggestedCode?: string, + originalCode?: string ) => { if (!pendingSelection || !files[activeFileIndex]) return; @@ -272,6 +273,7 @@ const ReviewApp: React.FC = () => { side: pendingSelection.side === 'additions' ? 'new' : 'old', text, suggestedCode, + originalCode, createdAt: Date.now(), author: identity, }; @@ -280,6 +282,23 @@ const ReviewApp: React.FC = () => { setPendingSelection(null); }, [pendingSelection, files, activeFileIndex, identity]); + // Edit annotation + const handleEditAnnotation = useCallback(( + id: string, + text?: string, + suggestedCode?: string, + originalCode?: string + ) => { + setAnnotations(prev => prev.map(ann => + ann.id === id ? { + ...ann, + ...(text !== undefined && { text }), + ...(suggestedCode !== undefined && { suggestedCode }), + ...(originalCode !== undefined && { originalCode }), + } : ann + )); + }, []); + // Delete annotation const handleDeleteAnnotation = useCallback((id: string) => { setAnnotations(prev => prev.filter(a => a.id !== id)); @@ -755,6 +774,7 @@ const ReviewApp: React.FC = () => { pendingSelection={pendingSelection} onLineSelection={handleLineSelection} onAddAnnotation={handleAddAnnotation} + onEditAnnotation={handleEditAnnotation} onSelectAnnotation={handleSelectAnnotation} onDeleteAnnotation={handleDeleteAnnotation} isViewed={viewedFiles.has(activeFile.path)} diff --git a/packages/review-editor/components/AnnotationToolbar.tsx b/packages/review-editor/components/AnnotationToolbar.tsx new file mode 100644 index 0000000..0c2a41c --- /dev/null +++ b/packages/review-editor/components/AnnotationToolbar.tsx @@ -0,0 +1,140 @@ +import React from 'react'; +import { ToolbarState } from '../hooks/useAnnotationToolbar'; +import { useTabIndent } from '../hooks/useTabIndent'; +import { formatLineRange } from '../utils/formatLineRange'; + +interface AnnotationToolbarProps { + toolbarState: ToolbarState; + toolbarRef: React.RefObject; + commentText: string; + setCommentText: (text: string) => void; + suggestedCode: string; + setSuggestedCode: React.Dispatch>; + showSuggestedCode: boolean; + setShowSuggestedCode: (show: boolean) => void; + isEditing?: boolean; + setShowCodeModal: (show: boolean) => void; + onSubmit: () => void; + onDismiss: () => void; + onCancel: () => void; +} + +/** Floating comment input form that appears after line selection */ +export const AnnotationToolbar: React.FC = ({ + toolbarState, + toolbarRef, + commentText, + setCommentText, + suggestedCode, + setSuggestedCode, + showSuggestedCode, + setShowSuggestedCode, + isEditing = false, + setShowCodeModal, + onSubmit, + onDismiss, + onCancel, +}) => { + const handleTabIndent = useTabIndent(setSuggestedCode); + + return ( +
+
+
+ + {isEditing ? 'Edit annotation' : formatLineRange(toolbarState.range.start, toolbarState.range.end)} + + +
+ +