diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx index 1b8d59426a..e769b4bdb9 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx @@ -1,11 +1,15 @@ -import type { DocMode } from '@blocksuite/affine/blocks'; +import { + type DocMode, + type NoteBlockModel, + NoteDisplayMode, +} from '@blocksuite/affine/blocks'; import type { AffineEditorContainer, DocTitle, EdgelessEditor, PageEditor, } from '@blocksuite/affine/presets'; -import { type Doc, Slot } from '@blocksuite/affine/store'; +import { type BlockModel, type Doc, Slot } from '@blocksuite/affine/store'; import clsx from 'clsx'; import type React from 'react'; import { @@ -126,10 +130,26 @@ export const BlocksuiteEditorContainer = forwardRef< const handleClickPageModeBlank = useCallback(() => { if (shared || page.readonly) return; - affineEditorContainerProxy.host?.std.command.exec( - 'appendParagraph' as never, - {} - ); + const std = affineEditorContainerProxy.host?.std; + if (!std) { + return; + } + const note = getLastNoteBlock(page); + if (note) { + const lastBlock = note.lastChild(); + if ( + lastBlock && + lastBlock.flavour === 'affine:paragraph' && + lastBlock.text?.length === 0 + ) { + std.command.exec('focusBlockEnd' as never, { + focusBlock: std.view.getBlock(lastBlock.id) as never, + }); + return; + } + } + + std.command.exec('appendParagraph' as never, {}); }, [affineEditorContainerProxy, page, shared]); return ( @@ -162,3 +182,32 @@ export const BlocksuiteEditorContainer = forwardRef< ); }); + +// copy from '@blocksuite/affine-shared/utils' +export function getLastNoteBlock(doc: Doc) { + let note: NoteBlockModel | null = null; + if (!doc.root) return null; + const { children } = doc.root; + for (let i = children.length - 1; i >= 0; i--) { + const child = children[i]; + if ( + matchFlavours(child, ['affine:note']) && + child.displayMode !== NoteDisplayMode.EdgelessOnly + ) { + note = child as NoteBlockModel; + break; + } + } + return note; +} +export function matchFlavours( + model: BlockModel | null, + expected: Key +): model is BlockSuite.BlockModels[Key[number]] { + return ( + !!model && + expected.some( + key => (model.flavour as keyof BlockSuite.BlockModels) === key + ) + ); +} diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/styles.css.ts b/packages/frontend/core/src/components/blocksuite/block-suite-editor/styles.css.ts index 32558b5aae..e6fdfb894d 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/styles.css.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/styles.css.ts @@ -1,6 +1,8 @@ import { cssVar } from '@toeverything/theme'; import { style, type StyleRule } from '@vanilla-extract/css'; +const editorBottomPadding = 32; + export const docEditorRoot = style({ display: 'block', background: cssVar('backgroundPrimaryColor'), @@ -29,10 +31,13 @@ export const docEditorGap = style({ display: 'block', width: '100%', margin: '0 auto', - paddingTop: 50, + // hack to cover the bottom padding of the editor + marginTop: -editorBottomPadding, + paddingTop: 50 + editorBottomPadding, paddingBottom: 50, cursor: 'text', flexGrow: 1, + zIndex: 1, }); const titleTagBasic = style({ diff --git a/tests/affine-local/e2e/blocksuite/editor.spec.ts b/tests/affine-local/e2e/blocksuite/editor.spec.ts index e408212008..459f30db19 100644 --- a/tests/affine-local/e2e/blocksuite/editor.spec.ts +++ b/tests/affine-local/e2e/blocksuite/editor.spec.ts @@ -64,3 +64,25 @@ test('link page is useable', async ({ page }) => { page.locator('.doc-title-container:has-text("page1")') ).toBeVisible(); }); + +test('append paragraph when click editor gap', async ({ page }) => { + await openHomePage(page); + await waitForEditorLoad(page); + await clickNewPageButton(page); + await waitForEditorLoad(page); + + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('test title'); + await page.keyboard.press('ArrowDown'); + await page.keyboard.insertText('test content'); + + const paragraph = page.locator('affine-paragraph'); + const numParagraphs = await paragraph.count(); + + await page.locator('[data-testid=page-editor-blank]').click(); + expect(await paragraph.count()).toBe(numParagraphs + 1); + + // click the gap again, should not append another paragraph + await page.locator('[data-testid=page-editor-blank]').click(); + expect(await paragraph.count()).toBe(numParagraphs + 1); +});