refactor: rename diff → annotations in plan review context (#173)

* refactor: rename misleading "diff" terminology to "annotations" in plan review context

The plan review flow used "diff" naming (exportDiff, diffOutput, .diff.md,
"Raw Diff", "Download .diff") for what is actually user annotations/feedback
on a plan. This was confusing since the code review flow legitimately uses
"diff" for actual git diffs. Renames all plan-review-context "diff" references
to "annotations" across code, UI labels, file extensions, and docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update stale exportDiff references in ANNOTATE.md

Missed during the diff→annotations rename. Updates two references
to exportDiff() → exportAnnotations() in the annotate flow docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: rename remaining "Download Diff" label and tab type in App.tsx

The quick-save dropdown button still showed "Download Diff" and the
initialExportTab state type still used 'diff' instead of 'annotations'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michael Ramos
2026-02-22 09:11:25 -08:00
committed by GitHub
parent 8a55be5013
commit 444934a654
10 changed files with 57 additions and 57 deletions
+1 -1
View File
@@ -222,7 +222,7 @@ interface Block {
- Horizontal rules (`---`)
- Paragraphs (default)
`exportDiff(blocks, annotations, globalAttachments)` generates human-readable feedback for Claude. Images are referenced by name: `[image-name] /tmp/path...`.
`exportAnnotations(blocks, annotations, globalAttachments)` generates human-readable feedback for Claude. Images are referenced by name: `[image-name] /tmp/path...`.
## Annotation System
@@ -92,7 +92,7 @@ Controls what the **Cmd/Ctrl+S** keyboard shortcut does.
| Option | Behavior |
|--------|----------|
| **Ask each time** (default) | Opens the Export dialog |
| **Download Diff** | Downloads the diff file directly |
| **Download Annotations** | Downloads the annotations file directly |
| **Obsidian** | Saves directly to your Obsidian vault (only shown if Obsidian is enabled) |
| **Bear** | Saves directly to Bear (only shown if Bear is enabled) |
+2 -2
View File
@@ -85,7 +85,7 @@ Users can also paste or upload images and attach them to annotations.
When the user clicks "Send Annotations" (or presses `Cmd+Enter`), the frontend:
1. Calls `exportDiff(blocks, annotations, globalAttachments)` from `packages/ui/utils/parser.ts` to generate a structured markdown summary
1. Calls `exportAnnotations(blocks, annotations, globalAttachments)` from `packages/ui/utils/parser.ts` to generate a structured markdown summary
2. POSTs the result to `/api/feedback`
The export format looks like:
@@ -165,7 +165,7 @@ Agent receives structured feedback, acts on it
| `packages/server/annotate.ts` | Annotate server implementation |
| `packages/editor/App.tsx` | Frontend — detects `annotateMode`, adjusts UI |
| `packages/ui/components/Viewer.tsx` | Markdown renderer + web-highlighter annotation |
| `packages/ui/utils/parser.ts` | `parseMarkdownToBlocks()` and `exportDiff()` |
| `packages/ui/utils/parser.ts` | `parseMarkdownToBlocks()` and `exportAnnotations()` |
| `apps/hook/commands/plannotator-annotate.md` | Claude Code slash command definition |
| `apps/opencode-plugin/commands/plannotator-annotate.md` | OpenCode slash command definition |
+15 -15
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useRef } from 'react';
import { parseMarkdownToBlocks, exportDiff, extractFrontmatter, Frontmatter } from '@plannotator/ui/utils/parser';
import { parseMarkdownToBlocks, exportAnnotations, extractFrontmatter, Frontmatter } from '@plannotator/ui/utils/parser';
import { Viewer, ViewerHandle } from '@plannotator/ui/components/Viewer';
import { AnnotationPanel } from '@plannotator/ui/components/AnnotationPanel';
import { ExportModal } from '@plannotator/ui/components/ExportModal';
@@ -365,7 +365,7 @@ const App: React.FC = () => {
const [shareBaseUrl, setShareBaseUrl] = useState<string | undefined>(undefined);
const [repoInfo, setRepoInfo] = useState<{ display: string; branch?: string } | null>(null);
const [showExportDropdown, setShowExportDropdown] = useState(false);
const [initialExportTab, setInitialExportTab] = useState<'share' | 'diff' | 'notes'>();
const [initialExportTab, setInitialExportTab] = useState<'share' | 'annotations' | 'notes'>();
const [noteSaveToast, setNoteSaveToast] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
const viewerRef = useRef<ViewerHandle>(null);
const containerRef = useRef<HTMLElement>(null);
@@ -584,7 +584,7 @@ const App: React.FC = () => {
// Include annotations as feedback if any exist (for OpenCode "approve with notes")
if (annotations.length > 0 || globalAttachments.length > 0) {
body.feedback = diffOutput;
body.feedback = annotationsOutput;
}
await fetch('/api/approve', {
@@ -606,7 +606,7 @@ const App: React.FC = () => {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
feedback: diffOutput,
feedback: annotationsOutput,
planSave: {
enabled: planSaveSettings.enabled,
...(planSaveSettings.customPath && { customPath: planSaveSettings.customPath }),
@@ -627,7 +627,7 @@ const App: React.FC = () => {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
feedback: diffOutput,
feedback: annotationsOutput,
annotations,
}),
});
@@ -733,19 +733,19 @@ const App: React.FC = () => {
// This is just a placeholder for future custom logic
};
const diffOutput = useMemo(() => exportDiff(blocks, annotations, globalAttachments), [blocks, annotations, globalAttachments]);
const annotationsOutput = useMemo(() => exportAnnotations(blocks, annotations, globalAttachments), [blocks, annotations, globalAttachments]);
// Quick-save handlers for export dropdown and keyboard shortcut
const handleDownloadDiff = () => {
const handleDownloadAnnotations = () => {
setShowExportDropdown(false);
const blob = new Blob([diffOutput], { type: 'text/plain' });
const blob = new Blob([annotationsOutput], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'annotations.diff';
a.download = 'annotations.md';
a.click();
URL.revokeObjectURL(url);
setNoteSaveToast({ type: 'success', message: 'Downloaded diff' });
setNoteSaveToast({ type: 'success', message: 'Downloaded annotations' });
setTimeout(() => setNoteSaveToast(null), 3000);
};
@@ -803,7 +803,7 @@ const App: React.FC = () => {
const bearOk = getBearSettings().enabled;
if (defaultApp === 'download') {
handleDownloadDiff();
handleDownloadAnnotations();
} else if (defaultApp === 'obsidian' && obsOk) {
handleQuickSaveToNotes('obsidian');
} else if (defaultApp === 'bear' && bearOk) {
@@ -819,7 +819,7 @@ const App: React.FC = () => {
}, [
showExport, showFeedbackPrompt, showClaudeCodeWarning, showAgentWarning,
showPermissionModeSetup, showUIFeaturesSetup, pendingPasteImage,
submitted, isApiMode, markdown, diffOutput,
submitted, isApiMode, markdown, annotationsOutput,
]);
// Close export dropdown on click outside
@@ -1009,13 +1009,13 @@ const App: React.FC = () => {
</button>
)}
<button
onClick={handleDownloadDiff}
onClick={handleDownloadAnnotations}
className="w-full text-left px-3 py-1.5 text-xs hover:bg-muted transition-colors flex items-center gap-2"
>
<svg className="w-3.5 h-3.5 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
Download Diff
Download Annotations
</button>
{isApiMode && isObsidianConfigured() && (
<button
@@ -1136,7 +1136,7 @@ const App: React.FC = () => {
onClose={() => { setShowExport(false); setInitialExportTab(undefined); }}
shareUrl={shareUrl}
shareUrlSize={shareUrlSize}
diffOutput={diffOutput}
annotationsOutput={annotationsOutput}
annotationCount={annotations.length}
taterSprite={taterMode ? <TaterSpritePullup /> : undefined}
sharingEnabled={sharingEnabled}
+4 -4
View File
@@ -312,11 +312,11 @@ export async function startPlannotatorServer(
// Save annotations and final snapshot (if enabled)
let savedPath: string | undefined;
if (planSaveEnabled) {
const diff = feedback || "";
if (diff) {
saveAnnotations(slug, diff, planSaveCustomPath);
const annotations = feedback || "";
if (annotations) {
saveAnnotations(slug, annotations, planSaveCustomPath);
}
savedPath = saveFinalSnapshot(slug, "approved", plan, diff, planSaveCustomPath);
savedPath = saveFinalSnapshot(slug, "approved", plan, annotations, planSaveCustomPath);
}
// Use permission mode from client request if provided, otherwise fall back to hook input
+9 -9
View File
@@ -65,35 +65,35 @@ export function savePlan(slug: string, content: string, customPath?: string | nu
}
/**
* Save annotations (diff) to disk.
* Save annotations to disk.
* Returns the full path to the saved file.
*/
export function saveAnnotations(slug: string, diffContent: string, customPath?: string | null): string {
export function saveAnnotations(slug: string, annotationsContent: string, customPath?: string | null): string {
const planDir = getPlanDir(customPath);
const filePath = join(planDir, `${slug}.diff.md`);
writeFileSync(filePath, diffContent, "utf-8");
const filePath = join(planDir, `${slug}.annotations.md`);
writeFileSync(filePath, annotationsContent, "utf-8");
return filePath;
}
/**
* Save the final snapshot on approve/deny.
* Combines plan and diff into a single file with status suffix.
* Combines plan and annotations into a single file with status suffix.
* Returns the full path to the saved file.
*/
export function saveFinalSnapshot(
slug: string,
status: "approved" | "denied",
plan: string,
diff: string,
annotations: string,
customPath?: string | null
): string {
const planDir = getPlanDir(customPath);
const filePath = join(planDir, `${slug}-${status}.md`);
// Combine plan with diff appended
// Combine plan with annotations appended
let content = plan;
if (diff && diff !== "No changes detected.") {
content += "\n\n---\n\n" + diff;
if (annotations && annotations !== "No changes detected.") {
content += "\n\n---\n\n" + annotations;
}
writeFileSync(filePath, content, "utf-8");
+21 -21
View File
@@ -1,8 +1,8 @@
/**
* Export Modal with tabs for Share, Raw Diff, and Notes
* Export Modal with tabs for Share, Annotations, and Notes
*
* Share tab (default): Shows shareable URL with copy button
* Raw Diff tab: Shows human-readable diff output with copy/download
* Annotations tab: Shows human-readable annotations output with copy/download
* Notes tab: Save plan to Obsidian/Bear without approving
*/
@@ -15,7 +15,7 @@ interface ExportModalProps {
onClose: () => void;
shareUrl: string;
shareUrlSize: string;
diffOutput: string;
annotationsOutput: string;
annotationCount: number;
taterSprite?: React.ReactNode;
sharingEnabled?: boolean;
@@ -24,7 +24,7 @@ interface ExportModalProps {
initialTab?: Tab;
}
type Tab = 'share' | 'diff' | 'notes';
type Tab = 'share' | 'annotations' | 'notes';
type SaveTarget = 'obsidian' | 'bear';
type SaveStatus = 'idle' | 'saving' | 'success' | 'error';
@@ -34,7 +34,7 @@ export const ExportModal: React.FC<ExportModalProps> = ({
onClose,
shareUrl,
shareUrlSize,
diffOutput,
annotationsOutput,
annotationCount,
taterSprite,
sharingEnabled = true,
@@ -42,7 +42,7 @@ export const ExportModal: React.FC<ExportModalProps> = ({
isApiMode = false,
initialTab,
}) => {
const defaultTab = initialTab || (sharingEnabled ? 'share' : 'diff');
const defaultTab = initialTab || (sharingEnabled ? 'share' : 'annotations');
const [activeTab, setActiveTab] = useState<Tab>(defaultTab);
const [copied, setCopied] = useState(false);
const [saveStatus, setSaveStatus] = useState<Record<SaveTarget, SaveStatus>>({ obsidian: 'idle', bear: 'idle' });
@@ -51,7 +51,7 @@ export const ExportModal: React.FC<ExportModalProps> = ({
// Reset tab when modal opens
useEffect(() => {
if (isOpen) {
setActiveTab(initialTab || (sharingEnabled ? 'share' : 'diff'));
setActiveTab(initialTab || (sharingEnabled ? 'share' : 'annotations'));
}
}, [isOpen, initialTab, sharingEnabled]);
@@ -82,9 +82,9 @@ export const ExportModal: React.FC<ExportModalProps> = ({
}
};
const handleCopyDiff = async () => {
const handleCopyAnnotations = async () => {
try {
await navigator.clipboard.writeText(diffOutput);
await navigator.clipboard.writeText(annotationsOutput);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (e) {
@@ -92,12 +92,12 @@ export const ExportModal: React.FC<ExportModalProps> = ({
}
};
const handleDownloadDiff = () => {
const blob = new Blob([diffOutput], { type: 'text/plain' });
const handleDownloadAnnotations = () => {
const blob = new Blob([annotationsOutput], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'annotations.diff';
a.download = 'annotations.md';
a.click();
URL.revokeObjectURL(url);
};
@@ -198,14 +198,14 @@ export const ExportModal: React.FC<ExportModalProps> = ({
</button>
)}
<button
onClick={() => setActiveTab('diff')}
onClick={() => setActiveTab('annotations')}
className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
activeTab === 'diff'
activeTab === 'annotations'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'
}`}
>
Raw Diff
Annotations
</button>
{showNotesTab && (
<button
@@ -372,25 +372,25 @@ export const ExportModal: React.FC<ExportModalProps> = ({
</div>
) : (
<pre className="bg-muted rounded-lg p-4 text-xs font-mono leading-relaxed overflow-x-auto whitespace-pre-wrap">
{diffOutput}
{annotationsOutput}
</pre>
)}
</div>
{/* Footer actions - only show for Raw Diff tab */}
{activeTab === 'diff' && (
{/* Footer actions - only show for Annotations tab */}
{activeTab === 'annotations' && (
<div className="p-4 border-t border-border flex justify-end gap-2">
<button
onClick={handleCopyDiff}
onClick={handleCopyAnnotations}
className="px-3 py-1.5 rounded-md text-xs font-medium bg-muted hover:bg-muted/80 transition-colors"
>
{copied ? 'Copied!' : 'Copy'}
</button>
<button
onClick={handleDownloadDiff}
onClick={handleDownloadAnnotations}
className="px-3 py-1.5 rounded-md text-xs font-medium bg-primary text-primary-foreground hover:opacity-90 transition-opacity"
>
Download .diff
Download Annotations
</button>
</div>
)}
+2 -2
View File
@@ -534,7 +534,7 @@ export const Settings: React.FC<SettingsProps> = ({ taterMode, onTaterModeChange
className="w-full px-3 py-2 bg-muted rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary/50 cursor-pointer"
>
<option value="ask">Ask each time</option>
<option value="download">Download Diff</option>
<option value="download">Download Annotations</option>
{obsidian.enabled && <option value="obsidian">Obsidian</option>}
{bear.enabled && <option value="bear">Bear</option>}
</select>
@@ -542,7 +542,7 @@ export const Settings: React.FC<SettingsProps> = ({ taterMode, onTaterModeChange
{defaultNotesApp === 'ask'
? 'Opens Export dialog with Notes tab'
: defaultNotesApp === 'download'
? `${navigator.platform?.includes('Mac') ? 'Cmd' : 'Ctrl'}+S downloads the diff file`
? `${navigator.platform?.includes('Mac') ? 'Cmd' : 'Ctrl'}+S downloads the annotations file`
: `${navigator.platform?.includes('Mac') ? 'Cmd' : 'Ctrl'}+S saves directly to ${defaultNotesApp === 'obsidian' ? 'Obsidian' : 'Bear'}`}
</div>
</div>
+1 -1
View File
@@ -243,7 +243,7 @@ export const parseMarkdownToBlocks = (markdown: string): Block[] => {
return blocks;
};
export const exportDiff = (blocks: Block[], annotations: any[], globalAttachments: ImageAttachment[] = []): string => {
export const exportAnnotations = (blocks: Block[], annotations: any[], globalAttachments: ImageAttachment[] = []): string => {
if (annotations.length === 0 && globalAttachments.length === 0) {
return 'No changes detected.';
}
+1 -1
View File
@@ -103,7 +103,7 @@ The TOC sidebar provides navigation for long documents.
**Export & Formatting:**
- [ ] Export diff shows annotations in readable format
- [ ] Export shows annotations in readable format
- [ ] Format follows: "- **DELETION** (line X): [original text]"
- [ ] Global comments appear at top of export
- [ ] All annotation types exported correctly