feat: per-author editor decoration types; proposals decorate in Claude blue/purple
This commit is contained in:
@@ -17,14 +17,20 @@ import { isAuthorable } from "./workspacePath";
|
|||||||
|
|
||||||
export class EditorProposalController implements vscode.Disposable, vscode.CodeLensProvider {
|
export class EditorProposalController implements vscode.Disposable, vscode.CodeLensProvider {
|
||||||
private readonly disposables: vscode.Disposable[] = [];
|
private readonly disposables: vscode.Disposable[] = [];
|
||||||
private readonly insertionDeco = vscode.window.createTextEditorDecorationType({
|
private readonly insDeco: Record<"human" | "claude", vscode.TextEditorDecorationType> = {
|
||||||
backgroundColor: new vscode.ThemeColor("diffEditor.insertedTextBackground"),
|
human: vscode.window.createTextEditorDecorationType({
|
||||||
});
|
backgroundColor: "rgba(63,185,80,0.14)", borderColor: "#3fb950",
|
||||||
private readonly deletionDeco = vscode.window.createTextEditorDecorationType({
|
border: "0 0 2px 0", borderStyle: "solid",
|
||||||
// a non-editable struck-red hint injected AFTER the insertion (INV-52)
|
}),
|
||||||
after: { color: new vscode.ThemeColor("gitDecoration.deletedResourceForeground") },
|
claude: vscode.window.createTextEditorDecorationType({
|
||||||
textDecoration: "none",
|
backgroundColor: "rgba(88,166,255,0.15)", borderColor: "#58a6ff",
|
||||||
});
|
border: "0 0 2px 0", borderStyle: "solid",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
private readonly delDeco: Record<"human" | "claude", vscode.TextEditorDecorationType> = {
|
||||||
|
human: vscode.window.createTextEditorDecorationType({ after: { color: "#f85149" } }),
|
||||||
|
claude: vscode.window.createTextEditorDecorationType({ after: { color: "#bc8cff" } }),
|
||||||
|
};
|
||||||
private readonly lensEmitter = new vscode.EventEmitter<void>();
|
private readonly lensEmitter = new vscode.EventEmitter<void>();
|
||||||
readonly onDidChangeCodeLenses = this.lensEmitter.event;
|
readonly onDidChangeCodeLenses = this.lensEmitter.event;
|
||||||
/** Pending debounce timers — one per URI — coalesce rapid-fire propose events
|
/** Pending debounce timers — one per URI — coalesce rapid-fire propose events
|
||||||
@@ -37,7 +43,7 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
|||||||
|
|
||||||
constructor(private readonly proposals: ProposalController) {
|
constructor(private readonly proposals: ProposalController) {
|
||||||
this.disposables.push(
|
this.disposables.push(
|
||||||
this.insertionDeco, this.deletionDeco, this.lensEmitter,
|
this.insDeco.human, this.insDeco.claude, this.delDeco.human, this.delDeco.claude, this.lensEmitter,
|
||||||
vscode.languages.registerCodeLensProvider({ language: "markdown" }, this),
|
vscode.languages.registerCodeLensProvider({ language: "markdown" }, this),
|
||||||
this.proposals.onDidChangeProposals(({ uri }) => this.scheduleApply(uri)),
|
this.proposals.onDidChangeProposals(({ uri }) => this.scheduleApply(uri)),
|
||||||
vscode.window.onDidChangeActiveTextEditor((ed) => ed && this.renderEditor(ed)),
|
vscode.window.onDidChangeActiveTextEditor((ed) => ed && this.renderEditor(ed)),
|
||||||
@@ -80,31 +86,43 @@ export class EditorProposalController implements vscode.Disposable, vscode.CodeL
|
|||||||
/** Decorate the editor for every applied proposal on its document (INV-52). */
|
/** Decorate the editor for every applied proposal on its document (INV-52). */
|
||||||
private renderEditor(editor: vscode.TextEditor): void {
|
private renderEditor(editor: vscode.TextEditor): void {
|
||||||
const doc = editor.document;
|
const doc = editor.document;
|
||||||
if (doc.languageId !== "markdown") {
|
const clearAll = () => {
|
||||||
editor.setDecorations(this.insertionDeco, []);
|
for (const a of ["human", "claude"] as const) {
|
||||||
editor.setDecorations(this.deletionDeco, []);
|
editor.setDecorations(this.insDeco[a], []);
|
||||||
return;
|
editor.setDecorations(this.delDeco[a], []);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if (doc.languageId !== "markdown") return clearAll();
|
||||||
const key = this.proposals.keyFor(doc);
|
const key = this.proposals.keyFor(doc);
|
||||||
const insertions: vscode.Range[] = [];
|
const ins: Record<"human" | "claude", vscode.Range[]> = { human: [], claude: [] };
|
||||||
const deletions: vscode.DecorationOptions[] = [];
|
const del: Record<"human" | "claude", vscode.DecorationOptions[]> = { human: [], claude: [] };
|
||||||
|
// Pending proposals — always Claude (INV: proposals are agent-authored).
|
||||||
for (const v of this.proposals.listProposals(doc)) {
|
for (const v of this.proposals.listProposals(doc)) {
|
||||||
if (v.anchorStart === null || v.original === undefined || !this.proposals.isApplied(key, v.id)) continue;
|
if (v.anchorStart === null || v.original === undefined || !this.proposals.isApplied(key, v.id)) continue;
|
||||||
const plan = decorationPlan(v.anchorStart, v.original, v.replacement);
|
const plan = decorationPlan(v.anchorStart, v.original, v.replacement);
|
||||||
for (const ins of plan.insertions) {
|
for (const i of plan.insertions) ins.claude.push(new vscode.Range(doc.positionAt(i.start), doc.positionAt(i.end)));
|
||||||
insertions.push(new vscode.Range(doc.positionAt(ins.start), doc.positionAt(ins.end)));
|
for (const d of plan.deletions) {
|
||||||
}
|
del.claude.push({
|
||||||
for (const del of plan.deletions) {
|
range: new vscode.Range(doc.positionAt(d.at), doc.positionAt(d.at)),
|
||||||
deletions.push({
|
renderOptions: { after: { contentText: ` ${d.text} `, textDecoration: "line-through" } },
|
||||||
range: new vscode.Range(doc.positionAt(del.at), doc.positionAt(del.at)),
|
|
||||||
renderOptions: { after: { contentText: ` ${del.text} `, textDecoration: "line-through" } },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor.setDecorations(this.insertionDeco, insertions);
|
// Committed changes-since-baseline are added in Task 7 (pushes into ins/del[author]).
|
||||||
editor.setDecorations(this.deletionDeco, deletions);
|
this.decorateCommitted(editor, ins, del); // no-op stub until Task 7
|
||||||
|
for (const a of ["human", "claude"] as const) {
|
||||||
|
editor.setDecorations(this.insDeco[a], ins[a]);
|
||||||
|
editor.setDecorations(this.delDeco[a], del[a]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Overridden in Task 7 to add committed (non-proposal) author-colored changes. */
|
||||||
|
private decorateCommitted(
|
||||||
|
_editor: vscode.TextEditor,
|
||||||
|
_ins: Record<"human" | "claude", vscode.Range[]>,
|
||||||
|
_del: Record<"human" | "claude", vscode.DecorationOptions[]>,
|
||||||
|
): void {}
|
||||||
|
|
||||||
/** CodeLensProvider: a `Accept ▾` / `Reject ▾` pair above each applied block. */
|
/** CodeLensProvider: a `Accept ▾` / `Reject ▾` pair above each applied block. */
|
||||||
provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
|
provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
|
||||||
if (document.languageId !== "markdown") return [];
|
if (document.languageId !== "markdown") return [];
|
||||||
|
|||||||
Reference in New Issue
Block a user