From a79f6f325b143bdce7d1de809ed20d2e1fa5de72 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sun, 14 Jun 2026 23:23:43 -0700 Subject: [PATCH] fix: keep review annotation button visible (#5383) --- .../rich-markdown-review-annotations.test.ts | 25 +++++++++ .../rich-markdown-review-annotations.ts | 55 +++++++++++++++++-- 2 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 src/renderer/src/components/editor/rich-markdown-review-annotations.test.ts diff --git a/src/renderer/src/components/editor/rich-markdown-review-annotations.test.ts b/src/renderer/src/components/editor/rich-markdown-review-annotations.test.ts new file mode 100644 index 00000000000..b0f163b7cc3 --- /dev/null +++ b/src/renderer/src/components/editor/rich-markdown-review-annotations.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest' +import { + getRichMarkdownAnnotationButtonLeft, + getRichMarkdownAnnotationButtonTop +} from './rich-markdown-review-annotations' + +describe('getRichMarkdownAnnotationButtonTop', () => { + it('keeps the add-note button below short visible selections', () => { + expect(getRichMarkdownAnnotationButtonTop(120, 500)).toBe(126) + }) + + it('clamps the add-note button inside the visible editor shell for long selections', () => { + expect(getRichMarkdownAnnotationButtonTop(760, 500)).toBe(470) + }) +}) + +describe('getRichMarkdownAnnotationButtonLeft', () => { + it('keeps the add-note button near the right edge when there is room', () => { + expect(getRichMarkdownAnnotationButtonLeft(700)).toBe(658) + }) + + it('clamps the add-note button inside narrow editor shells', () => { + expect(getRichMarkdownAnnotationButtonLeft(72)).toBe(42) + }) +}) diff --git a/src/renderer/src/components/editor/rich-markdown-review-annotations.ts b/src/renderer/src/components/editor/rich-markdown-review-annotations.ts index f2c9a29a029..947bb4a704f 100644 --- a/src/renderer/src/components/editor/rich-markdown-review-annotations.ts +++ b/src/renderer/src/components/editor/rich-markdown-review-annotations.ts @@ -10,6 +10,15 @@ import { import type { RichMarkdownReviewNotePosition } from './rich-markdown-review-note-layout' import { findRichMarkdownSelectedTextRanges } from './rich-markdown-review-text-ranges' +const RICH_MARKDOWN_ANNOTATION_BUTTON_SIZE_PX = 22 +const RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX = 8 +const RICH_MARKDOWN_ANNOTATION_SELECTION_GAP_PX = 6 +const RICH_MARKDOWN_ANNOTATION_MIN_LEFT_PX = 56 +const RICH_MARKDOWN_ANNOTATION_RIGHT_OFFSET_PX = 42 +const RICH_MARKDOWN_ANNOTATION_POPOVER_WIDTH_PX = 420 +const RICH_MARKDOWN_ANNOTATION_POPOVER_RIGHT_OFFSET_PX = 24 +const RICH_MARKDOWN_ANNOTATION_POPOVER_MIN_HEIGHT_PX = 220 + export type RichMarkdownCommentBlock = { key: string startLine: number @@ -235,6 +244,30 @@ function getCurrentRichMarkdownSelectionRect(root: HTMLElement): DOMRect | null return Array.from(range.getClientRects()).find((candidate) => candidate.width > 0) ?? null } +export function getRichMarkdownAnnotationButtonTop( + selectionBottomInRoot: number, + rootHeight: number +): number { + const preferredTop = selectionBottomInRoot + RICH_MARKDOWN_ANNOTATION_SELECTION_GAP_PX + const maxTop = Math.max( + RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX, + rootHeight - RICH_MARKDOWN_ANNOTATION_BUTTON_SIZE_PX - RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX + ) + return Math.max(RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX, Math.min(preferredTop, maxTop)) +} + +export function getRichMarkdownAnnotationButtonLeft(rootWidth: number): number { + const preferredLeft = Math.max( + RICH_MARKDOWN_ANNOTATION_MIN_LEFT_PX, + rootWidth - RICH_MARKDOWN_ANNOTATION_RIGHT_OFFSET_PX + ) + const maxLeft = Math.max( + RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX, + rootWidth - RICH_MARKDOWN_ANNOTATION_BUTTON_SIZE_PX - RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX + ) + return Math.min(preferredLeft, maxLeft) +} + export function getRichMarkdownAnnotationTarget( editor: Editor, root: HTMLElement @@ -251,10 +284,22 @@ export function getRichMarkdownAnnotationTarget( return null } const rootRect = root.getBoundingClientRect() - const popoverWidth = 420 - const left = Math.max(56, rootRect.width - popoverWidth - 24) - const buttonTop = Math.max(8, rect.bottom - rootRect.top + 6) - const popoverTop = Math.max(8, Math.min(buttonTop + 28, rootRect.height - 220)) + // Why: long selections can extend below the visible editor shell; keep the + // add-note affordance reachable instead of anchoring to hidden selection area. + const buttonTop = getRichMarkdownAnnotationButtonTop(rect.bottom - rootRect.top, rootRect.height) + const left = Math.max( + RICH_MARKDOWN_ANNOTATION_MIN_LEFT_PX, + rootRect.width - + RICH_MARKDOWN_ANNOTATION_POPOVER_WIDTH_PX - + RICH_MARKDOWN_ANNOTATION_POPOVER_RIGHT_OFFSET_PX + ) + const popoverTop = Math.max( + RICH_MARKDOWN_ANNOTATION_EDGE_PADDING_PX, + Math.min( + buttonTop + RICH_MARKDOWN_ANNOTATION_BUTTON_SIZE_PX + 6, + rootRect.height - RICH_MARKDOWN_ANNOTATION_POPOVER_MIN_HEIGHT_PX + ) + ) return { ...getRichMarkdownSelectionRange(editor), from: editor.state.selection.from, @@ -263,6 +308,6 @@ export function getRichMarkdownAnnotationTarget( top: popoverTop, left, buttonTop, - buttonLeft: Math.max(56, rootRect.width - 42) + buttonLeft: getRichMarkdownAnnotationButtonLeft(rootRect.width) } }