feat(f6): diff-view toggle on any file — global storage, untitled in-memory (#19)

F6 no longer requires a workspace file. It gets its own diffable predicate
(any file: or untitled:) decoupled from F2 isTracked, keys baselines by a
sha256 of the document URI, and stores them in VS Code GLOBAL storage
(context.globalStorageUri) — always present, never the repo (INV-19). Untitled
buffers have no durable identity → in-memory baseline only (lost on reload).

DiffViewController is now constructed before the workspace-root check and its
commands are always live (never stubbed) — the machine-landing advance wiring
stays in the with-root branch since the seam only fires on workspace files.
BaselineStore generalized to key-by-hash (Baseline.docPath → uri).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-11 07:47:05 -07:00
parent 9e7ef4e052
commit e617f504e7
4 changed files with 144 additions and 119 deletions
+18 -10
View File
@@ -44,6 +44,18 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
}),
);
// --- F6: diff-view toggle (Features #17 + #19) — workspace-INDEPENDENT ---
// F6 works on ANY diffable doc (file or untitled), so it is constructed
// regardless of whether a folder is open, and its commands are always live
// (never stubbed). Baseline lives in VS Code's per-extension GLOBAL storage
// (always present), never the repo (INV-19). The machine-landing advance is
// wired in the with-root branch below (landings only occur on workspace files
// through the seam). The controller self-wires baseline capture on open.
const baselineStorageDir = context.globalStorageUri?.fsPath;
const baselineStore = baselineStorageDir ? new BaselineStore(baselineStorageDir) : null;
const diffViewController = new DiffViewController(baselineStore);
context.subscriptions.push(diffViewController);
// --- F2: region-anchored threads (Feature #4) ---
const root = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (!root) {
@@ -66,11 +78,12 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
"cowriting.acceptProposal",
"cowriting.rejectProposal",
"cowriting.proposeAgentEdit",
"cowriting.toggleDiffView",
"cowriting.pinDiffBaseline",
]) {
context.subscriptions.push(vscode.commands.registerCommand(command, stub));
}
// F6 (toggleDiffView / pinDiffBaseline) is NOT stubbed here — it works
// without a folder (on untitled buffers and out-of-folder files); its real
// commands were registered above by DiffViewController.
return undefined;
}
const store = new CoauthorStore(root);
@@ -88,14 +101,10 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
const proposalController = new ProposalController(store, attributionController, root, versionGuard);
context.subscriptions.push(proposalController);
// --- F6: diff-view toggle (Feature #17) ---
// Baseline lives in VS Code workspace storage, never the repo (INV-19).
// storageUri can be undefined in odd host states → in-memory fallback (§6.5).
const storageDir = context.storageUri?.fsPath;
const baselineStore = storageDir ? new BaselineStore(storageDir) : null;
const diffViewController = new DiffViewController(baselineStore, root);
context.subscriptions.push(diffViewController);
// --- F6 machine-landing wiring (with-root only) ---
// The seam's single machine-landing signal advances the baseline (INV-18).
// The seam fires only on workspace files, so this wiring belongs here; the
// DiffViewController itself was constructed above (workspace-independent).
context.subscriptions.push(
attributionController.onDidApplyAgentEdit((e) => diffViewController.advance(e.document)),
);
@@ -245,7 +254,6 @@ export function activate(context: vscode.ExtensionContext): CowritingApi | undef
threadController.renderAll(doc);
attributionController.loadAll(doc);
proposalController.renderAll(doc);
diffViewController.ensureBaseline(doc);
}
};
vscode.workspace.textDocuments.forEach(renderIfOpen);