feat(f9): webview segmented mode toggle + authorship legend + author CSS

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-11 14:38:50 -07:00
parent 399e3c5f70
commit e0118bb835
3 changed files with 49 additions and 6 deletions
+13
View File
@@ -51,3 +51,16 @@ del, .cw-removed {
} }
pre.mermaid { text-align: center; background: transparent; } pre.mermaid { text-align: center; background: transparent; }
pre.mermaid[data-cw-error] { color: var(--vscode-errorForeground); } pre.mermaid[data-cw-error] { color: var(--vscode-errorForeground); }
/* F9 authorship mode — inline author tints + block-level fence badges + toggle. */
.cw-by-claude { background: var(--vscode-editorInfo-foreground, rgba(64, 120, 242, 0.18)); text-decoration: none; }
.cw-by-human { background: var(--vscode-gitDecoration-addedResourceForeground, rgba(46, 160, 67, 0.18)); text-decoration: none; }
.cw-blk.cw-by-claude, .cw-blk.cw-by-human, .cw-blk.cw-mixed { outline: 2px solid currentColor; outline-offset: 2px; background: transparent; }
.cw-seg {
background: transparent; color: var(--vscode-foreground);
border: 1px solid var(--vscode-panel-border); padding: 0 0.5em; cursor: pointer; font-size: 0.9em;
}
.cw-seg:first-child { border-radius: 3px 0 0 3px; }
.cw-seg:last-child { border-radius: 0 3px 3px 0; border-left: none; }
.cw-seg-on { background: var(--vscode-button-background); color: var(--vscode-button-foreground); }
#cw-legend .cw-swatch { padding: 0 0.4em; border-radius: 3px; }
+31 -6
View File
@@ -10,16 +10,29 @@ import mermaid from "mermaid";
// links it into the sealed shell via asWebviewUri). // links it into the sealed shell via asWebviewUri).
import "./preview.css"; import "./preview.css";
declare function acquireVsCodeApi(): { postMessage(m: unknown): void };
interface RenderMessage { interface RenderMessage {
type: "render"; type: "render";
mode: "changes" | "authorship";
html: string; html: string;
epoch: string; epoch?: string;
summary: { added: number; removed: number; changed: number }; summary?: { added: number; removed: number; changed: number };
legend?: { claude: boolean; human: boolean };
} }
const vscodeApi = acquireVsCodeApi();
const body = document.getElementById("cw-body")!; const body = document.getElementById("cw-body")!;
const header = document.getElementById("cw-epoch")!; const header = document.getElementById("cw-epoch")!;
const summary = document.getElementById("cw-summary")!; const summary = document.getElementById("cw-summary")!;
const legend = document.getElementById("cw-legend")!;
const segs = Array.from(document.querySelectorAll<HTMLButtonElement>(".cw-seg"));
for (const seg of segs) {
seg.addEventListener("click", () => {
vscodeApi.postMessage({ type: "setMode", mode: seg.dataset.mode });
});
}
function themeFor(): "dark" | "default" { function themeFor(): "dark" | "default" {
return document.body.classList.contains("vscode-dark") || return document.body.classList.contains("vscode-dark") ||
@@ -46,9 +59,21 @@ window.addEventListener("message", (event: MessageEvent<RenderMessage>) => {
const msg = event.data; const msg = event.data;
if (msg?.type !== "render") return; if (msg?.type !== "render") return;
body.innerHTML = msg.html; body.innerHTML = msg.html;
header.textContent = `Track changes since ${msg.epoch}`; for (const seg of segs) seg.classList.toggle("cw-seg-on", seg.dataset.mode === msg.mode);
summary.innerHTML = const authorship = msg.mode === "authorship";
`<span class="cw-add">+${msg.summary.added + msg.summary.changed}</span> ` + header.hidden = authorship;
`<span class="cw-del">${msg.summary.removed + msg.summary.changed}</span>`; summary.hidden = authorship;
legend.hidden = !authorship;
if (authorship) {
const parts: string[] = [];
if (msg.legend?.claude) parts.push('<span class="cw-by-claude cw-swatch">Claude</span>');
if (msg.legend?.human) parts.push('<span class="cw-by-human cw-swatch">You</span>');
legend.innerHTML = parts.join(" ") || "no attribution yet";
} else {
header.textContent = `Track changes since ${msg.epoch ?? ""}`;
summary.innerHTML =
`<span class="cw-add">+${(msg.summary?.added ?? 0) + (msg.summary?.changed ?? 0)}</span> ` +
`<span class="cw-del">${(msg.summary?.removed ?? 0) + (msg.summary?.changed ?? 0)}</span>`;
}
void renderMermaid(); void renderMermaid();
}); });
+5
View File
@@ -193,8 +193,13 @@ export class TrackChangesPreviewController implements vscode.Disposable {
</head> </head>
<body> <body>
<div id="cw-header"> <div id="cw-header">
<div id="cw-mode" role="group">
<button id="cw-mode-changes" class="cw-seg cw-seg-on" data-mode="changes">Track changes</button>
<button id="cw-mode-authorship" class="cw-seg" data-mode="authorship">Authorship</button>
</div>
<span id="cw-epoch">Track changes</span> <span id="cw-epoch">Track changes</span>
<span id="cw-summary"></span> <span id="cw-summary"></span>
<span id="cw-legend" hidden></span>
</div> </div>
<div id="cw-body"></div> <div id="cw-body"></div>
<script nonce="${nonce}" src="${scriptUri}"></script> <script nonce="${nonce}" src="${scriptUri}"></script>