diff --git a/media/preview.css b/media/preview.css index 0e10047..87aeb9e 100644 --- a/media/preview.css +++ b/media/preview.css @@ -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-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; } +#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. */ .cw-mermaid-legend { display: flex; gap: 0.6rem; font-size: 0.75em; opacity: 0.85; margin: 0.2rem 0 0.6rem; } diff --git a/media/preview.ts b/media/preview.ts index 4492857..d4e58c7 100644 --- a/media/preview.ts +++ b/media/preview.ts @@ -14,11 +14,10 @@ declare function acquireVsCodeApi(): { postMessage(m: unknown): void }; interface RenderMessage { type: "render"; - mode: "changes" | "authorship"; + mode: "on" | "off"; html: string; epoch?: string; - summary?: { added: number; removed: number; changed: number }; - legend?: { claude: boolean; human: boolean }; + summary?: { added: number; removed: number; proposals: number }; } const vscodeApi = acquireVsCodeApi(); @@ -26,13 +25,24 @@ const body = document.getElementById("cw-body")!; const header = document.getElementById("cw-epoch")!; const summary = document.getElementById("cw-summary")!; const legend = document.getElementById("cw-legend")!; -const segs = Array.from(document.querySelectorAll(".cw-seg")); +const annotationsEl = document.getElementById("cw-annotations") as HTMLInputElement | null; -for (const seg of segs) { - seg.addEventListener("click", () => { - vscodeApi.postMessage({ type: "setMode", mode: seg.dataset.mode }); - }); -} +// F10: the annotations on/off toggle. +annotationsEl?.addEventListener("change", () => { + 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(".cw-actions button"); + if (!btn) return; + const block = btn.closest(".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" { return document.body.classList.contains("vscode-dark") || @@ -59,21 +69,18 @@ window.addEventListener("message", (event: MessageEvent) => { const msg = event.data; if (msg?.type !== "render") return; body.innerHTML = msg.html; - for (const seg of segs) seg.classList.toggle("cw-seg-on", seg.dataset.mode === msg.mode); - const authorship = msg.mode === "authorship"; - header.hidden = authorship; - summary.hidden = authorship; - legend.hidden = !authorship; - if (authorship) { - const parts: string[] = []; - if (msg.legend?.claude) parts.push('Claude'); - if (msg.legend?.human) parts.push('You'); - legend.innerHTML = parts.join(" ") || "no attribution yet"; - } else { - header.textContent = `Track changes since ${msg.epoch ?? ""}`; + const on = msg.mode === "on"; + if (annotationsEl) annotationsEl.checked = on; + // Off-state is a clean preview: hide the review chrome. + header.hidden = !on; + summary.hidden = !on; + legend.hidden = true; + if (on) { + header.textContent = `Review since ${msg.epoch ?? ""}`; summary.innerHTML = - `+${(msg.summary?.added ?? 0) + (msg.summary?.changed ?? 0)} ` + - `−${(msg.summary?.removed ?? 0) + (msg.summary?.changed ?? 0)}`; + `+${msg.summary?.added ?? 0} ` + + `−${msg.summary?.removed ?? 0} ` + + `${msg.summary?.proposals ?? 0} proposal${(msg.summary?.proposals ?? 0) === 1 ? "" : "s"}`; } void renderMermaid(); });