Contribute rewrite Phase 7: retire DiffView
The Phase 3 preview-pane inline overlay and Phase 4 raw-pane gutter accent together cover the review affordance DiffView was the interim home for, so the toolbar toggle collapses and the surface goes. - frontend/src/components/DiffView.jsx deleted. - RFCView.jsx: drop the DiffView + marked imports, the reviewMode / reviewHTML state, the toggleReviewMode callback, the "Review changes" toolbar button, and the reviewMode ternary in render. editorEditable simplifies to mode === 'contribute' && canContribute. File-header docstring updated: "DiffView" → "preview-pane tracked-change overlay (§8.10)". - App.css: remove the .diff-view-wrapper and .diff-view-empty rules; relabel the DiffView section header as ChangeTooltip (which lives on as the Phase 3 overlay's hover affordance). Drop the dead .editor- content .tiptap rules — Tiptap had no remaining consumers after DiffView's deletion. The Contribute toolbar still renders the accepted/pending hint + Phase 6 Chat toggle; only the Review-changes button is removed. - MarkdownPreview.jsx: comment updated to drop the DiffView mention from the scoped-Marked-instance rationale. - SPEC.md: §8.10 retitled "Tracked-change markup" (was "...and the review-mode toggle"); the legacy-DiffView paragraph rewritten as a one-sentence retirement note. §8.1, §8.15, §9.4, §9.5 references to DiffView / review-mode cleaned up. §19.2's persistent-accepted-change candidate updated to point at the preview-pane overlay rather than DiffView. No DiffView orphans remain (grep confirms .btn-review-toggle is still the Discuss-mode "Show tracked changes" toggle, ChangeTooltip and trackedOverlay carry historical comments only, PRView's §19.2 comment is unrelated). Frontend reload shows no console errors. Backend integration suite: 125 passed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -635,8 +635,8 @@ The RFC view uses a three-column shape:
|
||||
|
||||
- **Left column** — the RFC catalog from §7, unchanged.
|
||||
- **Center column** — a thin breadcrumb strip at the top showing the
|
||||
current branch with a dropdown affordance; the editor (or diff view
|
||||
in review mode) below it; a prompt bar at the bottom for chat input.
|
||||
current branch with a dropdown affordance; the editor below it; a
|
||||
prompt bar at the bottom for chat input.
|
||||
- **Right column** — the chat thread for the currently-selected
|
||||
branch, with a change-card panel below it in contexts where editing
|
||||
is enabled (see §8.3).
|
||||
@@ -834,7 +834,7 @@ considered and rejected, not just what they accepted. To re-propose,
|
||||
ask the AI again — the new proposal lands as a new card with the
|
||||
declined predecessor still visible.
|
||||
|
||||
### 8.10 Tracked-change markup and the review-mode toggle
|
||||
### 8.10 Tracked-change markup
|
||||
|
||||
Two visual layers carry change information in the Contribute split.
|
||||
The first is a paragraph-margin marker on the left raw-source pane —
|
||||
@@ -871,12 +871,10 @@ marked span surfaces a tooltip with the change's type badge (`ai` or
|
||||
`was_edited_before_accept` flag where set, the user prompt and
|
||||
selection-quote that drove the change, and the AI's `reason`.
|
||||
|
||||
DiffView is the legacy read-only render surface invoked via the
|
||||
Contribute-mode toolbar toggle (§8.15) — a full-editor swap-in that
|
||||
reads the same `changes` table. It is retained as an interim path while
|
||||
the split-pane layers mature, and is slated for retirement once
|
||||
the raw-pane gutter and the preview-pane inline overlay together cover
|
||||
its review affordance; at that point the toolbar toggle collapses.
|
||||
DiffView, the legacy full-editor swap-in for inspecting accepted
|
||||
changes in context, was retired in the Contribute rewrite once the
|
||||
raw-pane gutter and the preview-pane inline overlay together covered
|
||||
its review affordance.
|
||||
|
||||
### 8.11 Manual edits and collisions with AI proposals
|
||||
|
||||
@@ -1047,10 +1045,9 @@ conversation produced it on.
|
||||
|
||||
These affordances are scoped to the current branch:
|
||||
the selection tooltip (elaborated in §8.12 as the range-thread
|
||||
entry point); the review-mode toggle and `DiffView` for inspecting
|
||||
accepted changes in context (§8.10); the discuss-mode banner
|
||||
indicating read-only status; the `<change>` / `<original>` /
|
||||
`<proposed>` / `<reason>` AI protocol (§18).
|
||||
entry point); the discuss-mode banner indicating read-only status;
|
||||
the `<change>` / `<original>` / `<proposed>` / `<reason>` AI
|
||||
protocol (§18).
|
||||
|
||||
The following are implementation-level details that the build session
|
||||
will decide:
|
||||
@@ -1249,12 +1246,11 @@ inline with the edit branches in the dropdown.
|
||||
|
||||
The document pane uses the same Tiptap editor as active RFCs, in
|
||||
read-only mode by default, identical to §8.2. The toolbar shape, the
|
||||
selection-tooltip from §8.12, the §8.13 flag affordances, and the
|
||||
review-mode/DiffView toggle from §8.10 all map across. The discuss
|
||||
vs. contribute mode toggle from §8.3 exists; the canonical body is
|
||||
in discuss mode like main on an active RFC, and the "Start
|
||||
Contributing" affordance cuts an edit branch per §9.5 rather than
|
||||
flipping mode inline.
|
||||
selection-tooltip from §8.12, and the §8.13 flag affordances all map
|
||||
across. The discuss vs. contribute mode toggle from §8.3 exists; the
|
||||
canonical body is in discuss mode like main on an active RFC, and the
|
||||
"Start Contributing" affordance cuts an edit branch per §9.5 rather
|
||||
than flipping mode inline.
|
||||
|
||||
§8.7's read-only fallbacks apply identically. Anonymous viewers see
|
||||
the full body, the selection tooltip works but its submit is
|
||||
@@ -1285,10 +1281,11 @@ On the edit branch, everything from §8.4 through §8.14 applies
|
||||
unchanged. The per-branch chat (§8.4), AI proposals materializing
|
||||
as `<change>` rows, accept/decline/edit-before-accept (§8.9),
|
||||
manual-edit flushes (§8.6), the change-card panel (§8.8), range and
|
||||
paragraph sub-threads (§8.12), flags (§8.13), DiffView (§8.10),
|
||||
stale-change handling (§8.11). The "branch" abstraction §8 was
|
||||
written against is the meta-repo edit branch; the machinery does
|
||||
not care whether the underlying repo is per-RFC or meta.
|
||||
paragraph sub-threads (§8.12), flags (§8.13), the tracked-change
|
||||
markup (§8.10), stale-change handling (§8.11). The "branch"
|
||||
abstraction §8 was written against is the meta-repo edit branch;
|
||||
the machinery does not care whether the underlying repo is per-RFC
|
||||
or meta.
|
||||
|
||||
Opening a PR is §10.1's gesture, with the meta-repo branch as
|
||||
source and the meta repo as target. The PR creation modal (§10.2),
|
||||
@@ -2907,13 +2904,13 @@ binding.
|
||||
file-rename bot sequence, the redirect handling for any links
|
||||
into the old slug, and the cache and threads migration.
|
||||
- **Persistent accepted-change markup for returning contributors.**
|
||||
§8.10 commits the editor's tracked-change markup to session-
|
||||
local scope and points returning-contributor needs at DiffView.
|
||||
A future session may revisit this with a per-user, per-branch
|
||||
seen-cursor for accepted changes (mirroring §10.3's PR seen-
|
||||
cursor) — markup persisting across reloads, dismissible with a
|
||||
"mark as seen" gesture. Triggered by evidence of contributors
|
||||
asking for it, not ahead of evidence.
|
||||
§8.10 commits the editor's tracked-change markup to session-local
|
||||
scope via the preview-pane overlay (default-on in Contribute,
|
||||
toggle-gated in Discuss). A future session may revisit this with a
|
||||
per-user, per-branch seen-cursor for accepted changes (mirroring
|
||||
§10.3's PR seen-cursor) — markup persisting across reloads,
|
||||
dismissible with a "mark as seen" gesture. Triggered by evidence
|
||||
of contributors asking for it, not ahead of evidence.
|
||||
- **AI participation as a notification source.** Topic 13 settled
|
||||
that user-driven events carry the underlying user as actor and
|
||||
system-generated events carry null. AI participant completions
|
||||
|
||||
+7
-39
@@ -431,31 +431,6 @@
|
||||
.editor-content {
|
||||
max-width: 720px; margin: 0 auto; outline: none;
|
||||
}
|
||||
.editor-content .tiptap {
|
||||
outline: none; font-size: 15px; line-height: 1.75; color: #1a1a1a;
|
||||
}
|
||||
.editor-content .tiptap h1 { font-size: 22px; font-weight: 700; margin: 24px 0 12px; }
|
||||
.editor-content .tiptap h2 { font-size: 17px; font-weight: 600; margin: 20px 0 8px; }
|
||||
.editor-content .tiptap h3 { font-size: 15px; font-weight: 600; margin: 16px 0 6px; }
|
||||
.editor-content .tiptap p { margin: 0 0 12px; }
|
||||
.editor-content .tiptap ul, .editor-content .tiptap ol { padding-left: 24px; }
|
||||
.editor-content .tiptap code { background: #f0f0ee; padding: 1px 5px; border-radius: 3px; font-size: 13px; }
|
||||
|
||||
.editor-content .tiptap .selection-highlight {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
border-radius: 2px;
|
||||
outline: 1px solid rgba(99, 102, 241, 0.3);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.editor-content .tiptap .tracked-delete {
|
||||
background: #fee2e2; color: #991b1b; text-decoration: line-through;
|
||||
border-radius: 2px; padding: 1px 2px; cursor: pointer;
|
||||
}
|
||||
.editor-content .tiptap .tracked-insert {
|
||||
background: #dcfce7; color: #166534;
|
||||
border-radius: 2px; padding: 1px 2px; cursor: pointer;
|
||||
}
|
||||
|
||||
/* CodeMirror 6 source editor (Contribute mode). */
|
||||
.cm-source-editor {
|
||||
flex: 1; min-height: 0;
|
||||
@@ -540,11 +515,11 @@
|
||||
background: rgba(99, 102, 241, 0.22);
|
||||
}
|
||||
|
||||
/* Tracked-change overlay (Phase 3, §8.10) — preview-pane equivalents of
|
||||
the .editor-content .tiptap rules above. The Contribute pane shows
|
||||
these by default; the Discuss pane gates them behind a toolbar
|
||||
toggle. Hovering surfaces a ChangeTooltip with the source message
|
||||
and reason. */
|
||||
/* Tracked-change overlay (Phase 3, §8.10) — green/red decorations on
|
||||
accepted-change spans inside the rendered preview. The Contribute
|
||||
pane shows these by default; the Discuss pane gates them behind a
|
||||
toolbar toggle. Hovering surfaces a ChangeTooltip with the source
|
||||
message and reason. */
|
||||
.markdown-preview .tracked-delete {
|
||||
background: #fee2e2; color: #991b1b; text-decoration: line-through;
|
||||
border-radius: 2px; padding: 1px 2px; cursor: pointer;
|
||||
@@ -1057,16 +1032,9 @@
|
||||
font-size: 13px; font-weight: 600; cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── DiffView ──────────────────────────────────────────────────── */
|
||||
/* ── ChangeTooltip (shared between the Phase 3 preview-pane overlay
|
||||
and the §8.10 hover affordance) ─────────────────────────────────── */
|
||||
|
||||
.diff-view-wrapper {
|
||||
flex: 1; overflow-y: auto;
|
||||
padding: 32px 48px;
|
||||
}
|
||||
.diff-view-empty {
|
||||
font-size: 13px; color: #999;
|
||||
text-align: center; padding: 24px;
|
||||
}
|
||||
.diff-tooltip {
|
||||
background: #fff; border: 1px solid #e5e5e5;
|
||||
border-radius: 8px; padding: 10px 12px;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// DiffView.jsx — the §8.10 read-only render surface for accepted changes.
|
||||
//
|
||||
// In contribute mode, a toolbar toggle replaces the editor with this
|
||||
// view. We reconstruct the markup for every accepted change in branch
|
||||
// history by reading the `changes` table (passed in as `changes`) plus
|
||||
// the current rendered HTML; hovering any tracked span surfaces a
|
||||
// tooltip with the change's type/model/prompt/reason context.
|
||||
|
||||
import { useState, useCallback } from 'react'
|
||||
import ChangeTooltip from './ChangeTooltip.jsx'
|
||||
|
||||
export default function DiffView({ html, changes, messages }) {
|
||||
const [tooltip, setTooltip] = useState(null)
|
||||
const handleMouseMove = useCallback((e) => {
|
||||
const span = e.target.closest('[data-change-id]')
|
||||
if (span) {
|
||||
const id = span.getAttribute('data-change-id')
|
||||
const change = changes.find(c => String(c.id) === String(id))
|
||||
if (change) {
|
||||
setTooltip({ change, position: { x: e.clientX, y: e.clientY } })
|
||||
return
|
||||
}
|
||||
}
|
||||
setTooltip(null)
|
||||
}, [changes])
|
||||
|
||||
const acceptedCount = changes.filter(c => c.state === 'accepted').length
|
||||
|
||||
return (
|
||||
<div className="diff-view-wrapper">
|
||||
{acceptedCount === 0 && (
|
||||
<div className="diff-view-empty">
|
||||
No accepted changes yet on this branch. Accept proposals from the
|
||||
change panel to see them rendered here in place.
|
||||
</div>
|
||||
)}
|
||||
<div className="editor-content">
|
||||
<div
|
||||
className="tiptap diff-view-document"
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={() => setTooltip(null)}
|
||||
dangerouslySetInnerHTML={{ __html: html }}
|
||||
/>
|
||||
</div>
|
||||
{tooltip && <ChangeTooltip change={tooltip.change} messages={messages} position={tooltip.position} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ function escapeHtml(s) {
|
||||
}
|
||||
|
||||
// Scoped Marked instance so the mermaid-fence renderer doesn't leak
|
||||
// to other call sites (Editor.jsx, DiffView review HTML, etc.).
|
||||
// to any other call site that might construct its own Marked.
|
||||
const previewMarked = new Marked({
|
||||
renderer: {
|
||||
code({ text, lang }) {
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
// super-drafts. The discuss-vs-contribute flip on non-main / non-edit
|
||||
// branches per §8.3 carries over unchanged.
|
||||
//
|
||||
// The active-RFC and super-draft surfaces share their editor,
|
||||
// chat, change-card, DiffView, selection-tooltip, and PR-modal
|
||||
// machinery; the only branchings are "Start Contributing" (which
|
||||
// The active-RFC and super-draft surfaces share their editor, chat,
|
||||
// change-card, preview-pane tracked-change overlay (§8.10), selection-
|
||||
// tooltip, and PR-modal machinery; the only branchings are
|
||||
// "Start Contributing" (which
|
||||
// dispatches to promote-to-branch for active main and start-edit-branch
|
||||
// for a super-draft's canonical body per §9.5) and the metadata pane
|
||||
// (super-draft-only per §9.5).
|
||||
@@ -36,11 +37,9 @@ import {
|
||||
import MarkdownSourceEditor from './MarkdownSourceEditor.jsx'
|
||||
import MarkdownPreview from './MarkdownPreview.jsx'
|
||||
import SelectionTooltip from './SelectionTooltip.jsx'
|
||||
import { marked } from 'marked'
|
||||
import PromptBar from './PromptBar.jsx'
|
||||
import ChatPanel from './ChatPanel.jsx'
|
||||
import ChangePanel, { diffWords } from './ChangePanel.jsx'
|
||||
import DiffView from './DiffView.jsx'
|
||||
import PRModal from './PRModal.jsx'
|
||||
import GraduateDialog from './GraduateDialog.jsx'
|
||||
import { claimOwnership } from '../api'
|
||||
@@ -92,8 +91,6 @@ export default function RFCView({ viewer }) {
|
||||
// selection is sourced from window.getSelection() inside the
|
||||
// MarkdownPreview surface — see MarkdownPreview's onSelectionChange.
|
||||
const [selection, setSelection] = useState(null)
|
||||
const [reviewMode, setReviewMode] = useState(false)
|
||||
const [reviewHTML, setReviewHTML] = useState('')
|
||||
// Phase 3 — Discuss-mode opt-in for the §8.10 tracked-change overlay
|
||||
// in the single-pane preview. Contribute mode is default-on (the
|
||||
// pane exists for editorial review of your own work); Discuss mode
|
||||
@@ -151,7 +148,6 @@ export default function RFCView({ viewer }) {
|
||||
setChanges([])
|
||||
setPendingDiscussChanges([])
|
||||
setManualPending(null)
|
||||
setReviewMode(false)
|
||||
setDiscussShowChanges(false)
|
||||
setSelection(null)
|
||||
setMode('discuss')
|
||||
@@ -467,24 +463,6 @@ export default function RFCView({ viewer }) {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const toggleReviewMode = useCallback(() => {
|
||||
setReviewMode(prev => {
|
||||
if (!prev) {
|
||||
// CM6 is the contribute-mode editor in Phase 1, so source HTML
|
||||
// comes from rendering the current markdown via marked. The
|
||||
// per-session inline tracked-change spans that Tiptap accumulated
|
||||
// are gone for now; Phase 3's preview pane is the proper home
|
||||
// for tracked changes anyway.
|
||||
const editor = editorRef.current
|
||||
const md = typeof editor?.getDoc === 'function'
|
||||
? editor.getDoc()
|
||||
: (branchView?.body || '')
|
||||
setReviewHTML(marked.parse(md))
|
||||
}
|
||||
return !prev
|
||||
})
|
||||
}, [branchView])
|
||||
|
||||
// ── Branch dropdown navigation ─────────────────────────────────────────
|
||||
const onPickBranch = useCallback((name) => {
|
||||
if (name === branchParam) return
|
||||
@@ -505,7 +483,7 @@ export default function RFCView({ viewer }) {
|
||||
|
||||
const canContribute = branchView.capabilities?.can_contribute && branchParam !== 'main'
|
||||
const canChangeSettings = branchView.capabilities?.can_change_branch_settings
|
||||
const editorEditable = mode === 'contribute' && canContribute && !reviewMode
|
||||
const editorEditable = mode === 'contribute' && canContribute
|
||||
const showPromptBar = !!viewer
|
||||
|
||||
const inDiscuss = mode === 'discuss'
|
||||
@@ -671,14 +649,6 @@ export default function RFCView({ viewer }) {
|
||||
)}
|
||||
{showContributeToolbar && (
|
||||
<div className="editor-toolbar">
|
||||
<button
|
||||
type="button"
|
||||
className={`btn-review-toggle${reviewMode ? ' active' : ''}`}
|
||||
onClick={toggleReviewMode}
|
||||
title="Toggle DiffView: read-only render of accepted changes in context"
|
||||
>
|
||||
{reviewMode ? '← Back to editing' : 'Review changes'}
|
||||
</button>
|
||||
<span className="editor-toolbar-hint">
|
||||
{changes.filter(c => c.state === 'accepted').length} accepted ·{' '}
|
||||
{pendingCount} pending
|
||||
@@ -722,64 +692,58 @@ export default function RFCView({ viewer }) {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{reviewMode ? (
|
||||
<DiffView html={reviewHTML} changes={changes} messages={messages} />
|
||||
) : (
|
||||
<>
|
||||
{editorEditable ? (
|
||||
<div className="editor-split">
|
||||
<div className="editor-split-pane editor-split-raw">
|
||||
<MarkdownSourceEditor
|
||||
initialDoc={editorContent}
|
||||
editorRef={editorRef}
|
||||
onUpdate={handleEditorDocUpdate}
|
||||
/>
|
||||
</div>
|
||||
<div className="editor-split-pane editor-split-preview">
|
||||
<MarkdownPreview
|
||||
content={previewContent}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
changes={changes}
|
||||
messages={messages}
|
||||
showTrackedChanges
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
{editorEditable ? (
|
||||
<div className="editor-split">
|
||||
<div className="editor-split-pane editor-split-raw">
|
||||
<MarkdownSourceEditor
|
||||
initialDoc={editorContent}
|
||||
editorRef={editorRef}
|
||||
onUpdate={handleEditorDocUpdate}
|
||||
/>
|
||||
</div>
|
||||
<div className="editor-split-pane editor-split-preview">
|
||||
<MarkdownPreview
|
||||
content={editorContent}
|
||||
content={previewContent}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
className="markdown-preview-solo"
|
||||
changes={changes}
|
||||
messages={messages}
|
||||
showTrackedChanges={inDiscuss && discussShowChanges}
|
||||
showTrackedChanges
|
||||
/>
|
||||
)}
|
||||
<SelectionTooltip
|
||||
selection={selection}
|
||||
onAsk={handleTooltipAsk}
|
||||
onFlag={handleTooltipFlag}
|
||||
disabled={isStreaming || !viewer}
|
||||
models={models}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={setSelectedModel}
|
||||
/>
|
||||
{showPromptBar ? (
|
||||
<PromptBar
|
||||
selection={selection?.text || null}
|
||||
onSubmit={handlePrompt}
|
||||
disabled={isStreaming}
|
||||
models={models}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={setSelectedModel}
|
||||
discussMode={inDiscuss}
|
||||
/>
|
||||
) : (
|
||||
<div className="readonly-bar">
|
||||
Read-only view. <a href="/auth/login">Sign in</a> to participate.
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<MarkdownPreview
|
||||
content={editorContent}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
className="markdown-preview-solo"
|
||||
changes={changes}
|
||||
messages={messages}
|
||||
showTrackedChanges={inDiscuss && discussShowChanges}
|
||||
/>
|
||||
)}
|
||||
<SelectionTooltip
|
||||
selection={selection}
|
||||
onAsk={handleTooltipAsk}
|
||||
onFlag={handleTooltipFlag}
|
||||
disabled={isStreaming || !viewer}
|
||||
models={models}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={setSelectedModel}
|
||||
/>
|
||||
{showPromptBar ? (
|
||||
<PromptBar
|
||||
selection={selection?.text || null}
|
||||
onSubmit={handlePrompt}
|
||||
disabled={isStreaming}
|
||||
models={models}
|
||||
selectedModel={selectedModel}
|
||||
onModelChange={setSelectedModel}
|
||||
discussMode={inDiscuss}
|
||||
/>
|
||||
) : (
|
||||
<div className="readonly-bar">
|
||||
Read-only view. <a href="/auth/login">Sign in</a> to participate.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user