feat(webview): Accept/Reject + dropdown, rejectAll, control parity (#64, INV-53)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-26 08:07:06 -07:00
parent 38053239fa
commit f4594daa6f
8 changed files with 118 additions and 9 deletions
+12
View File
@@ -144,6 +144,18 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
}),
);
// #64 (INV-53): reject every pending proposal on the active doc in one gesture.
context.subscriptions.push(
vscode.commands.registerCommand("cowriting.rejectAllProposals", async () => {
const doc = vscode.window.activeTextEditor?.document;
if (!doc || doc.languageId !== "markdown") {
void vscode.window.showWarningMessage("Cowriting: open a Markdown document to reject its proposals.");
return;
}
await trackChangesPreviewController.rejectAll(doc);
}),
);
// --- F6 machine-landing wiring — now for ANY authorable doc ---
// The seam's single machine-landing signal advances the F6 baseline (INV-18);
// the seam can now fire on out-of-folder files too, so wire it unconditionally.
+8 -2
View File
@@ -754,8 +754,14 @@ function proposalBlockHtml(p: ProposalView, render: (src: string) => string): st
const after = `<ins class="cw-add">${safe(p.replacement)}</ins>`;
const actions =
`<span class="cw-actions">` +
`<button class="cw-accept" data-action="accept">✓</button>` +
`<button class="cw-reject" data-action="reject">✗</button>` +
`<span class="cw-btngroup">` +
`<button class="cw-accept" data-action="accept">Accept</button>` +
`<button class="cw-caret" data-action="acceptAll" title="Accept all pending proposals">▾</button>` +
`</span>` +
`<span class="cw-btngroup">` +
`<button class="cw-reject" data-action="reject">Reject</button>` +
`<button class="cw-caret" data-action="rejectAll" title="Reject all pending proposals">▾</button>` +
`</span>` +
`</span>`;
return `<div class="cw-proposal${unanchored}" data-proposal-id="${md.utils.escapeHtml(p.id)}">${actions}${before}${after}</div>`;
}
+16 -3
View File
@@ -44,7 +44,8 @@ type ToolbarMsg =
| { type: "pinBaseline" }
| { type: "askClaude"; scope: "document" }
| { type: "askClaude"; scope: "selection"; start: number; end: number }
| { type: "acceptAll" };
| { type: "acceptAll" }
| { type: "rejectAll" };
export class TrackChangesPreviewController implements vscode.Disposable {
private readonly disposables: vscode.Disposable[] = [];
@@ -190,8 +191,7 @@ export class TrackChangesPreviewController implements vscode.Disposable {
.acceptById(this.proposals.keyFor(document), m.proposalId)
.then(() => this.refresh(document));
} else if (m?.type === "reject" && m.proposalId) {
this.proposals.rejectById(this.proposals.keyFor(document), m.proposalId);
this.refresh(document);
void this.proposals.rejectByIdInPlace(this.proposals.keyFor(document), m.proposalId).then(() => this.refresh(document));
} else if (m?.type === "pinBaseline") {
// F6 baseline store re-render arrives via the onDidChangeBaseline subscription.
this.diffView.pin(document);
@@ -202,6 +202,8 @@ export class TrackChangesPreviewController implements vscode.Disposable {
} else if (m?.type === "acceptAll") {
// #46 (INV-42): batch-accept every pending proposal on this doc, then report.
void this.acceptAll(document);
} else if (m?.type === "rejectAll") {
void this.rejectAll(document);
}
}
@@ -222,6 +224,17 @@ export class TrackChangesPreviewController implements vscode.Disposable {
);
}
/** #64 (INV-53): revert every pending proposal on the document; report the count. */
async rejectAll(document: vscode.TextDocument): Promise<void> {
const { reverted } = await this.proposals.rejectAll(document);
this.refresh(document);
if (reverted > 0) {
void vscode.window.showInformationMessage(
`Cowriting: rejected ${reverted} proposal${reverted === 1 ? "" : "s"}.`,
);
}
}
/**
* Where the inline Ask-Claude input anchors: a range edit pins to its selection;
* a whole-document edit pins to the editor's cursor line if this document is the