mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 03:56:23 +08:00
fix(editor): invalid caret in note-edgeless-block on focus (#14229)
### Problem ●In edgeless mode, when starting to edit, `note-block` exhibits two types of invalid caret behavior: (1)**Title Region Misalignment**: Clicking on the title region incorrectly generates the caret in the first line of the note content, rather than in the title itself. (2)**Vanishing Caret at Line End**: When clicking in the empty space beyond the end of a text section, the caret appears momentarily at the line's end but disappears immediately. ●The following video demonstrates these issues: https://github.com/user-attachments/assets/db9c2c50-709f-4d32-912c-0f01841d2024 ### Solution ●**Title Click Interception**: Added a check to determine if the click coordinates fall in the title region. If so, the caret positioning is now handled by a dedicated logic path. Otherwise, it falls back to the existing note-content logic as before. ●**Range Normalization**: When the generated `range.startContainer` is not a `TextNode`, try to find a most appropriate `TextNode` and update the `range` accordingly. ### After ●The video below shows the behavior after this fix. https://github.com/user-attachments/assets/b2f70b64-1fc6-4049-8379-8bcf3a488a05 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Clicking a page block title no longer creates unwanted paragraphs and reliably focuses the title. * Paragraph creation now occurs only when needed and focus is applied only after successful creation. * Click coordinates are clamped to container bounds to prevent misplaced cursors or focus. * **Improvements** * Caret normalization: clicks place the caret at the last meaningful text position for consistent single-cursor behavior. * **Tests** * Added end-to-end coverage for caret placement and focus transitions. * New ratio-based click/double-click test utilities and a helper for double-clicking note bodies. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -6,6 +6,8 @@ import type { ParagraphBlockComponent } from '@blocksuite/affine-block-paragraph
|
||||
import type { BlockComponent } from '@blocksuite/std';
|
||||
import { expect, type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { dblclickLocatorByRatio } from './utils';
|
||||
|
||||
const EDGELESS_TOOLBAR_WIDGET = 'edgeless-toolbar-widget';
|
||||
export const ZERO_WIDTH_FOR_EMPTY_LINE =
|
||||
process.env.BROWSER === 'webkit' ? '\u200C' : '\u200B';
|
||||
@@ -65,6 +67,11 @@ export function locateEditorContainer(page: Page, editorIndex = 0) {
|
||||
return page.locator('[data-affine-editor-container]').nth(editorIndex);
|
||||
}
|
||||
|
||||
export async function dblclickNoteBody(page: Page) {
|
||||
const note = page.locator('affine-edgeless-note');
|
||||
await dblclickLocatorByRatio(page, note, { yRatio: 0.7 });
|
||||
}
|
||||
|
||||
export function locateDocTitle(page: Page, editorIndex = 0) {
|
||||
return locateEditorContainer(page, editorIndex).locator('doc-title');
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { expect } from '@playwright/test';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
export async function waitForLogMessage(
|
||||
@@ -70,3 +71,48 @@ export async function isContainedInBoundingBox(
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Click at a specific position relative to a locator's bounding box.
|
||||
* * Ratios are NOT clamped:
|
||||
* - 0 ~ 1 : inside the bounding box
|
||||
* - < 0 : outside (left / top of the box)
|
||||
* - > 1 : outside (right / bottom of the box)
|
||||
*
|
||||
* @param locator The locator to click
|
||||
* @param options Optional click position ratios
|
||||
* @param options.xRatio Horizontal ratio relative to box width (not clamped), default is 0.5 (center)
|
||||
* @param options.yRatio Vertical ratio relative to box height (not clamped), default is 0.5 (center)
|
||||
*/
|
||||
export async function clickLocatorByRatio(
|
||||
page: Page,
|
||||
locator: Locator,
|
||||
{ xRatio = 0.5, yRatio = 0.5 } = {}
|
||||
) {
|
||||
const box = await getLocatorBox(locator);
|
||||
|
||||
await page.mouse.click(
|
||||
box.x + box.width * xRatio,
|
||||
box.y + box.height * yRatio
|
||||
);
|
||||
}
|
||||
|
||||
export async function dblclickLocatorByRatio(
|
||||
page: Page,
|
||||
locator: Locator,
|
||||
{ xRatio = 0.5, yRatio = 0.5 } = {}
|
||||
) {
|
||||
const box = await getLocatorBox(locator);
|
||||
|
||||
await page.mouse.dblclick(
|
||||
box.x + box.width * xRatio,
|
||||
box.y + box.height * yRatio
|
||||
);
|
||||
}
|
||||
|
||||
async function getLocatorBox(locator: Locator) {
|
||||
await expect(locator).toBeVisible();
|
||||
const box = await locator.boundingBox();
|
||||
if (!box) throw new Error(`error getting locator's bounding box`);
|
||||
return box;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user