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:
+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