diff --git a/src/editor/plugins/mark-popover.ts b/src/editor/plugins/mark-popover.ts index 7bb4e581..562deb9d 100644 --- a/src/editor/plugins/mark-popover.ts +++ b/src/editor/plugins/mark-popover.ts @@ -1215,6 +1215,10 @@ class MarkPopoverController { } private renderSuggestion(mark: Mark): void { + if (mark.kind === 'flagged') { + this.renderFlag(mark); + return; + } this.popover.innerHTML = ''; const header = document.createElement('div'); @@ -1286,6 +1290,45 @@ class MarkPopoverController { this.popover.appendChild(actions); } + private renderFlag(mark: Mark): void { + this.popover.innerHTML = ''; + + const header = document.createElement('div'); + header.className = 'mark-popover-header'; + header.textContent = `Flagged by ${getActorName(mark.by)}`; + + const body = document.createElement('div'); + body.className = 'mark-popover-body'; + const note = (mark.data as { note?: string } | undefined)?.note; + body.textContent = note || mark.quote || ''; + + const actions = document.createElement('div'); + actions.className = 'mark-popover-actions'; + + if (canCommentInRuntime()) { + const removeButton = document.createElement('button'); + removeButton.type = 'button'; + removeButton.textContent = 'Remove flag'; + installTouchSafeButton(removeButton, () => { + deleteMark(this.view, mark.id); + this.close(); + }); + actions.appendChild(removeButton); + } + + const closeButton = document.createElement('button'); + closeButton.type = 'button'; + closeButton.textContent = 'Close'; + installTouchSafeButton(closeButton, () => { + this.close(); + }); + actions.appendChild(closeButton); + + this.popover.appendChild(header); + this.popover.appendChild(body); + this.popover.appendChild(actions); + } + private cacheActionRange(): void { const { from, to } = this.view.state.selection; if (from === to) return; diff --git a/src/editor/plugins/marks.ts b/src/editor/plugins/marks.ts index 910604cb..1356af9b 100644 --- a/src/editor/plugins/marks.ts +++ b/src/editor/plugins/marks.ts @@ -3110,9 +3110,14 @@ function createDecorations( switch (mark.kind) { case 'authored': case 'approved': - case 'flagged': continue; + case 'flagged': { + style = STYLES.flagged; + cssClass = `mark-flagged ${isActive ? 'mark-active' : ''}`; + break; + } + case 'comment': { const data = mark.data as CommentData; if (data?.resolved) continue; @@ -3223,6 +3228,15 @@ function injectGlowStyles(): void { 100% { box-shadow: none; } } + /* Flag glow (dusty rose, box-shadow only so the flag background persists) */ + .mark-flagged.proof-mark-new { + animation-name: proof-flag-glow; + } + @keyframes proof-flag-glow { + 0% { box-shadow: 0 0 8px rgba(252, 165, 165, 0.8); } + 100% { box-shadow: none; } + } + /* Refresh transition animations */ .proof-refreshing { opacity: 0.3; transition: opacity 200ms ease-out; } .proof-refreshed { opacity: 1; transition: opacity 300ms ease-in; }