F10 SLICE-3: webview on/off switch + ✓/✗ click→postMessage + proposal CSS (#29)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-12 00:20:00 -07:00
parent f8b36c3452
commit 28c5e9d334
2 changed files with 49 additions and 30 deletions
+19 -7
View File
@@ -56,14 +56,26 @@ pre.mermaid[data-cw-error] { color: var(--vscode-errorForeground); }
.cw-by-claude { background: var(--vscode-editorInfo-foreground, rgba(64, 120, 242, 0.18)); text-decoration: none; } .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-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-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; } #cw-legend .cw-swatch { padding: 0 0.4em; border-radius: 3px; }
#cw-summary .cw-prop { opacity: 0.85; }
/* F10 interactive review — annotations toggle + ✓/✗ proposal blocks. */
#cw-toggle { display: inline-flex; align-items: center; gap: 0.35em; cursor: pointer; }
.cw-proposal {
position: relative;
border-left: 3px solid var(--vscode-charts-blue, #4daafc);
background: color-mix(in srgb, var(--vscode-charts-blue, #4daafc) 12%, transparent);
padding: 0.4em 0.6em; margin: 0.4em 0; border-radius: 3px;
}
.cw-proposal-unanchored { border-left-style: dashed; opacity: 0.85; }
.cw-actions { position: absolute; top: 0.2em; right: 0.4em; display: inline-flex; gap: 0.25em; }
.cw-actions button {
cursor: pointer; border: 1px solid var(--vscode-button-border, transparent);
border-radius: 3px; font-size: 0.9em; line-height: 1; padding: 0.1em 0.35em;
background: var(--vscode-button-secondaryBackground); color: var(--vscode-button-secondaryForeground);
}
.cw-accept:hover { background: var(--vscode-testing-iconPassed, #2ea043); color: #fff; }
.cw-reject:hover { background: var(--vscode-errorForeground, #f14c4c); color: #fff; }
/* F7.1 (#22) intra-diagram mermaid diff legend. */ /* F7.1 (#22) intra-diagram mermaid diff legend. */
.cw-mermaid-legend { display: flex; gap: 0.6rem; font-size: 0.75em; opacity: 0.85; margin: 0.2rem 0 0.6rem; } .cw-mermaid-legend { display: flex; gap: 0.6rem; font-size: 0.75em; opacity: 0.85; margin: 0.2rem 0 0.6rem; }
+30 -23
View File
@@ -14,11 +14,10 @@ declare function acquireVsCodeApi(): { postMessage(m: unknown): void };
interface RenderMessage { interface RenderMessage {
type: "render"; type: "render";
mode: "changes" | "authorship"; mode: "on" | "off";
html: string; html: string;
epoch?: string; epoch?: string;
summary?: { added: number; removed: number; changed: number }; summary?: { added: number; removed: number; proposals: number };
legend?: { claude: boolean; human: boolean };
} }
const vscodeApi = acquireVsCodeApi(); const vscodeApi = acquireVsCodeApi();
@@ -26,13 +25,24 @@ 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 legend = document.getElementById("cw-legend")!;
const segs = Array.from(document.querySelectorAll<HTMLButtonElement>(".cw-seg")); const annotationsEl = document.getElementById("cw-annotations") as HTMLInputElement | null;
for (const seg of segs) { // F10: the annotations on/off toggle.
seg.addEventListener("click", () => { annotationsEl?.addEventListener("change", () => {
vscodeApi.postMessage({ type: "setMode", mode: seg.dataset.mode }); vscodeApi.postMessage({ type: "setMode", mode: annotationsEl.checked ? "on" : "off" });
}); });
}
// F10: delegated ✓/✗ accept/reject of pending proposals (routed back to the F4 seam).
body.addEventListener("click", (e) => {
const btn = (e.target as HTMLElement)?.closest<HTMLElement>(".cw-actions button");
if (!btn) return;
const block = btn.closest<HTMLElement>(".cw-proposal");
const id = block?.dataset.proposalId;
const action = btn.dataset.action;
if (id && (action === "accept" || action === "reject")) {
vscodeApi.postMessage({ type: action, proposalId: id });
}
});
function themeFor(): "dark" | "default" { function themeFor(): "dark" | "default" {
return document.body.classList.contains("vscode-dark") || return document.body.classList.contains("vscode-dark") ||
@@ -59,21 +69,18 @@ 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;
for (const seg of segs) seg.classList.toggle("cw-seg-on", seg.dataset.mode === msg.mode); const on = msg.mode === "on";
const authorship = msg.mode === "authorship"; if (annotationsEl) annotationsEl.checked = on;
header.hidden = authorship; // Off-state is a clean preview: hide the review chrome.
summary.hidden = authorship; header.hidden = !on;
legend.hidden = !authorship; summary.hidden = !on;
if (authorship) { legend.hidden = true;
const parts: string[] = []; if (on) {
if (msg.legend?.claude) parts.push('<span class="cw-by-claude cw-swatch">Claude</span>'); header.textContent = `Review since ${msg.epoch ?? ""}`;
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 = summary.innerHTML =
`<span class="cw-add">+${(msg.summary?.added ?? 0) + (msg.summary?.changed ?? 0)}</span> ` + `<span class="cw-add">+${msg.summary?.added ?? 0}</span> ` +
`<span class="cw-del">${(msg.summary?.removed ?? 0) + (msg.summary?.changed ?? 0)}</span>`; `<span class="cw-del">${msg.summary?.removed ?? 0}</span> ` +
`<span class="cw-prop">${msg.summary?.proposals ?? 0} proposal${(msg.summary?.proposals ?? 0) === 1 ? "" : "s"}</span>`;
} }
void renderMermaid(); void renderMermaid();
}); });