feat: authorship + change annotations in the built-in Markdown preview (D3/D21, PUC-3); cowriting.annotations toggle

Task 7 of the native-surfaces migration plan. Pure previewAnnotations.ts
(annotateSource + cowritingMarkdownItPlugin) reuses trackChangesModel.ts's
sentinel discipline (#33/#47), generalized to a 3-way ins-claude/ins-human/del
tag (exported injectSentinels/sentinelsToSpans, colorByAuthor unaffected,
84/84 existing tests pass unmodified). Host hook in extension.ts wires
registry/diffView/attribution/proposals/config into the plugin, exposed via
CowritingApi.extendMarkdownIt + a previewAnnotationHost test seam.
cowriting.annotations setting + toggleAnnotations command/menu. Q4 mermaid
(previewScripts + options.highlight fence override) implemented per the
proven bierner.markdown-mermaid pattern; intra-diagram diff augmentation
scoped out (unverifiable via the structural E2E harness) — see
task-7-report.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-07-02 14:23:11 -07:00
parent c9975ba9e6
commit 17fc01e8d8
9 changed files with 576 additions and 29 deletions
+43
View File
@@ -0,0 +1,43 @@
/*
* Task 7 (D3/D21, PUC-3) — authorship + change annotations for the BUILT-IN VS
* Code Markdown preview (`markdown.previewStyles`). Same F10 vocabulary as the
* (sunsetting) custom webview's `media/preview.css`: style = operation
* (underline = inserted, strikethrough = removed), color = author (human
* green, Claude blue) — `cw-del` has no author variant (a single fixed
* struck-red mark; see `previewAnnotations.ts`/`trackChangesModel.ts`'s "del"
* sentinel tag). Light-theme defaults below; `body.vscode-dark` (also applied
* on high-contrast dark) overrides with the same palette the webview preview
* already ships, for continuity across both review surfaces.
*/
.cw-ins-claude {
background: rgba(9, 105, 218, 0.12);
border-bottom: 2px solid #0969da;
text-decoration: none;
}
.cw-ins-human {
background: rgba(26, 127, 55, 0.12);
border-bottom: 2px solid #1a7f37;
text-decoration: none;
}
.cw-del {
background: rgba(207, 34, 46, 0.1);
text-decoration: line-through;
text-decoration-color: #cf222e;
opacity: 0.75;
}
body.vscode-dark .cw-ins-claude,
body.vscode-high-contrast:not(.vscode-high-contrast-light) .cw-ins-claude {
background: rgba(88, 166, 255, 0.15);
border-bottom-color: #58a6ff;
}
body.vscode-dark .cw-ins-human,
body.vscode-high-contrast:not(.vscode-high-contrast-light) .cw-ins-human {
background: rgba(63, 185, 80, 0.14);
border-bottom-color: #3fb950;
}
body.vscode-dark .cw-del,
body.vscode-high-contrast:not(.vscode-high-contrast-light) .cw-del {
background: rgba(248, 81, 73, 0.11);
text-decoration-color: #f85149;
}
+53
View File
@@ -0,0 +1,53 @@
/**
* Task 7 (Q4 mermaid check, D3/D21): the built-in VS Code Markdown preview does
* not render mermaid fences on its own — `previewAnnotations.ts`'s
* `cowritingMarkdownItPlugin` teaches ```mermaid fences to render as
* `<pre class="mermaid">SRC</pre>` (via `options.highlight`, the same
* extension point the built-in preview's own fence rule already calls); this
* script (contributed via `markdown.previewScripts`) is what actually turns
* those into diagrams — mermaid needs a DOM, so it runs here, in the preview's
* webview, never in the extension host. Bundled by esbuild as a standalone
* IIFE → out/media/preview-mermaid.js, so mermaid never enters the host
* bundle (matching the sealed webview's `media/preview.ts` precedent).
*
* Contributed preview scripts are reloaded on every content change (VS Code
* docs), so running at module top-level is sufficient; the
* `vscode.markdown.updateContent` listener is added defensively to also cover
* in-place content updates that don't reload the script (mirrors the proven
* `bierner.markdown-mermaid` extension's own approach).
*
* SCOPE NOTE (Q4 finding, recorded per the migration plan's Task 7 Step 2.6):
* this renders the CURRENT diagram only — it does NOT re-emit a changed
* diagram through `mermaidDiff.ts`'s intra-diagram diff/legend augmentation
* (the F7.1 feature the sealed webview preview has). That augmentation needs a
* block-level (not word-hunk-level) diff pass wired into `annotateSource`,
* which was judged out of scope for this increment — it cannot be verified by
* the automated E2E harness (the built-in preview's DOM/CSP sandbox isn't
* queryable from a host test) and shipping it unverified risked a silent
* regression. Basic mermaid rendering (this file) IS wired and is exercised by
* the same proven pattern as the real `bierner.markdown-mermaid` extension.
*/
import mermaid from "mermaid";
function theme(): "dark" | "default" {
return document.body.classList.contains("vscode-dark") || document.body.classList.contains("vscode-high-contrast")
? "dark"
: "default";
}
async function run(): Promise<void> {
const nodes = Array.from(document.querySelectorAll<HTMLElement>("pre.mermaid"));
if (nodes.length === 0) return;
mermaid.initialize({ startOnLoad: false, theme: theme(), securityLevel: "strict" });
try {
await mermaid.run({ nodes });
} catch {
// mermaid.run already marks failed nodes; ensure a visible chip per failure.
for (const n of nodes) {
if (!n.querySelector("svg")) n.setAttribute("data-cw-error", "true");
}
}
}
window.addEventListener("vscode.markdown.updateContent", () => void run());
void run();