Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ function isDirectSingleCommentHighlightHit(target: EventTarget | null): boolean
return getCommentHighlightThreadIds(target).length === 1;
}

function isDirectTrackedChangeHit(target: EventTarget | null): boolean {
if (!(target instanceof Element)) return false;
return target.closest(TRACK_CHANGE_SELECTOR) != null;
}

function resolveTrackChangeThreadId(target: EventTarget | null): string | null {
if (!(target instanceof Element)) {
return null;
Expand Down Expand Up @@ -220,11 +225,11 @@ function shouldIgnoreRepeatClickOnActiveComment(
return false;
}

// Direct clicks on commented text should place a caret at the clicked
// position and let the comments plugin infer the active thread from the
// resulting selection. Only preserve the pointerdown short-circuit for
// nearby non-text surfaces, such as split-run gaps.
if (isDirectSingleCommentHighlightHit(target)) {
// Direct clicks on single-thread comment text or tracked-change text should
// place a caret at the clicked position and let comment/thread activation be
// inferred from the resulting selection. Only preserve the pointerdown
// short-circuit for nearby non-text surfaces, such as split-run gaps.
if (isDirectSingleCommentHighlightHit(target) || isDirectTrackedChangeHit(target)) {
return false;
}

Expand Down Expand Up @@ -2274,7 +2279,9 @@ export class EditorInputManager {
}

#handleSingleCommentHighlightClick(event: PointerEvent, target: HTMLElement | null, editor: Editor): boolean {
if (isDirectSingleCommentHighlightHit(target)) {
// Direct hits on inline annotated text should not be intercepted here.
// Let generic click-to-position place the caret at the clicked pixel.
if (isDirectSingleCommentHighlightHit(target) || isDirectTrackedChangeHit(target)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ describe('EditorInputManager - single-thread comment highlight clicks', () => {
expect(viewportHost.setPointerCapture).toHaveBeenCalled();
});

it('activates a tracked-change decoration when it owns the clicked visual surface', () => {
it('lets direct clicks on a tracked-change decoration fall through to generic caret placement', () => {
mockEditor.state.comments$.activeThreadId = 'comment-2';

const trackedChange = document.createElement('span');
Expand All @@ -248,12 +248,32 @@ describe('EditorInputManager - single-thread comment highlight clicks', () => {

dispatchPointerDown(trackedChange);

expect(mockEditor.commands.setCursorById).toHaveBeenCalledWith('change-1', {
activeCommentId: 'change-1',
});
expect(resolvePointerPositionHit).not.toHaveBeenCalled();
expect(mockEditor.state.tr.setSelection).not.toHaveBeenCalled();
expect(viewportHost.setPointerCapture).not.toHaveBeenCalled();
expect(mockEditor.commands.setCursorById).not.toHaveBeenCalled();
expect(resolvePointerPositionHit).toHaveBeenCalled();
expect(TextSelection.create as unknown as Mock).toHaveBeenCalled();
expect(mockEditor.state.tr.setSelection).toHaveBeenCalled();
expect(viewportHost.setPointerCapture).toHaveBeenCalled();
});

it('lets repeat direct clicks on the active tracked-change decoration reposition caret', () => {
mockEditor.state.comments$.activeThreadId = 'change-1';

const trackedChange = document.createElement('span');
trackedChange.className = 'track-delete-dec highlighted';
trackedChange.setAttribute('data-track-change-id', 'change-1');
viewportHost.appendChild(trackedChange);

dispatchPointerDown(trackedChange);

expect(mockEditor.emit).not.toHaveBeenCalledWith(
'commentsUpdate',
expect.objectContaining({ activeCommentId: 'change-1' }),
);
expect(mockEditor.commands.setCursorById).not.toHaveBeenCalled();
expect(resolvePointerPositionHit).toHaveBeenCalled();
expect(TextSelection.create as unknown as Mock).toHaveBeenCalled();
expect(mockEditor.state.tr.setSelection).toHaveBeenCalled();
expect(viewportHost.setPointerCapture).toHaveBeenCalled();
});

it('activates a nearby single-thread highlight when a split-run gap receives the pointer event', () => {
Expand Down
Loading