From 11dfc1d1df4b9025587e33a07f6fe7c95c41416b Mon Sep 17 00:00:00 2001 From: L-Sun Date: Thu, 8 May 2025 10:21:32 +0000 Subject: [PATCH] feat(editor): insert embed doc with quick search by default (#12165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close [BS-3404](https://linear.app/affine-design/issue/BS-3404/通过embed-doc时,插入的doc还是card-view,应该要默认embed-view) ## Summary by CodeRabbit - **New Features** - Added the ability to insert and display embedded synced documents, supporting different link types based on editor mode and user preferences. - Introduced new UI interactions and view options for embedded synced documents in edgeless mode. - **Bug Fixes** - Updated UI selectors and preference keys to ensure consistent behavior and correct application of user settings. - **Tests** - Added and updated end-to-end tests for embedding synced documents, including header interactions and viewport fitting. - Improved test coverage for quick search insertion and edgeless embed synced doc features. - **Chores** - Renamed settings and updated exports to align with new embedding functionality. --- .../commands/insert-link-by-quick-search.ts | 25 +++++++++-- .../commands/insert-embed-linked-doc.ts | 1 + .../embed-linked-doc-block/configs/toolbar.ts | 2 +- .../embed-synced-doc-block/commands/index.ts | 1 + .../commands/insert-embed-synced-doc.ts | 20 +++++++++ .../embed-synced-doc-block/configs/toolbar.ts | 2 +- .../src/embed-synced-doc-block/index.ts | 1 + .../src/services/editor-setting-service.ts | 2 +- .../edgeless-embed-synced-doc-header.tsx | 2 + .../core/src/modules/dnd/services/index.ts | 3 +- .../e2e/blocksuite/edgeless/embed.spec.ts | 2 + .../e2e/blocksuite/embed/synced.spec.ts | 45 +++++++++++++++++++ tests/affine-local/e2e/quick-search.spec.ts | 8 ++-- tests/kit/src/utils/editor.ts | 11 +++++ 14 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/index.ts create mode 100644 blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/insert-embed-synced-doc.ts diff --git a/blocksuite/affine/blocks/bookmark/src/commands/insert-link-by-quick-search.ts b/blocksuite/affine/blocks/bookmark/src/commands/insert-link-by-quick-search.ts index ef398ad1d9..27174bcd05 100644 --- a/blocksuite/affine/blocks/bookmark/src/commands/insert-link-by-quick-search.ts +++ b/blocksuite/affine/blocks/bookmark/src/commands/insert-link-by-quick-search.ts @@ -2,9 +2,14 @@ import { insertEmbedIframeWithUrlCommand } from '@blocksuite/affine-block-embed' import { type InsertedLinkType, insertEmbedLinkedDocCommand, + insertEmbedSyncedDocCommand, type LinkableFlavour, } from '@blocksuite/affine-block-embed-doc'; -import { QuickSearchProvider } from '@blocksuite/affine-shared/services'; +import { + DocModeProvider, + EditorSettingProvider, + QuickSearchProvider, +} from '@blocksuite/affine-shared/services'; import type { Command } from '@blocksuite/std'; import { insertBookmarkCommand } from './insert-bookmark'; @@ -26,12 +31,26 @@ export const insertLinkByQuickSearchCommand: Command< // add linked doc if ('docId' in result) { - std.command.exec(insertEmbedLinkedDocCommand, { + const editorMode = std.get(DocModeProvider).getEditorMode(); + const editorSettings = std.get(EditorSettingProvider); + let flavour: LinkableFlavour = 'affine:embed-linked-doc'; + if (editorMode === 'edgeless') { + flavour = + editorSettings.setting$.value.docCanvasPreferView ?? flavour; + } + + const insertCommand = + flavour === 'affine:embed-linked-doc' + ? insertEmbedLinkedDocCommand + : insertEmbedSyncedDocCommand; + + std.command.exec(insertCommand, { docId: result.docId, params: result.params, }); + return { - flavour: 'affine:embed-linked-doc', + flavour, }; } diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts b/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts index cbd9e99cb5..36fbbf4f92 100644 --- a/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts +++ b/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/commands/insert-embed-linked-doc.ts @@ -5,6 +5,7 @@ import type { Command } from '@blocksuite/std'; export type LinkableFlavour = | 'affine:bookmark' | 'affine:embed-linked-doc' + | 'affine:embed-synced-doc' | 'affine:embed-iframe' | 'affine:embed-figma' | 'affine:embed-github' diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/configs/toolbar.ts b/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/configs/toolbar.ts index fd00703c72..c188dd25b4 100644 --- a/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/configs/toolbar.ts +++ b/blocksuite/affine/blocks/embed-doc/src/embed-linked-doc-block/configs/toolbar.ts @@ -224,7 +224,7 @@ const conversionsActionGroup = { ) { const editorSetting = ctx.std.getOptional(EditorSettingProvider); editorSetting?.set?.( - 'docDropCanvasPreferView', + 'docCanvasPreferView', 'affine:embed-synced-doc' ); } diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/index.ts b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/index.ts new file mode 100644 index 0000000000..651a0912c8 --- /dev/null +++ b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/index.ts @@ -0,0 +1 @@ +export { insertEmbedSyncedDocCommand } from './insert-embed-synced-doc'; diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/insert-embed-synced-doc.ts b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/insert-embed-synced-doc.ts new file mode 100644 index 0000000000..8a38eb50e5 --- /dev/null +++ b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/commands/insert-embed-synced-doc.ts @@ -0,0 +1,20 @@ +import { insertEmbedCard } from '@blocksuite/affine-block-embed'; +import type { EmbedCardStyle, ReferenceParams } from '@blocksuite/affine-model'; +import type { Command } from '@blocksuite/std'; + +export const insertEmbedSyncedDocCommand: Command< + { + docId: string; + params?: ReferenceParams; + }, + { blockId: string } +> = (ctx, next) => { + const { docId, params, std } = ctx; + const flavour = 'affine:embed-synced-doc'; + const targetStyle: EmbedCardStyle = 'syncedDoc'; + const props: Record = { pageId: docId }; + if (params) props.params = params; + const blockId = insertEmbedCard(std, { flavour, targetStyle, props }); + if (!blockId) return; + next({ blockId }); +}; diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/configs/toolbar.ts b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/configs/toolbar.ts index c5ebea0918..fb2bc4c9af 100644 --- a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/configs/toolbar.ts +++ b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/configs/toolbar.ts @@ -171,7 +171,7 @@ const conversionsActionGroup = { ) { const editorSetting = ctx.std.getOptional(EditorSettingProvider); editorSetting?.set?.( - 'docDropCanvasPreferView', + 'docCanvasPreferView', 'affine:embed-linked-doc' ); } diff --git a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/index.ts b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/index.ts index 5dcd792702..48149b5c9c 100644 --- a/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/index.ts +++ b/blocksuite/affine/blocks/embed-doc/src/embed-synced-doc-block/index.ts @@ -1,4 +1,5 @@ export * from './adapters'; +export * from './commands'; export * from './configs'; export * from './edgeless-clipboard-config'; export * from './embed-synced-doc-block'; diff --git a/blocksuite/affine/shared/src/services/editor-setting-service.ts b/blocksuite/affine/shared/src/services/editor-setting-service.ts index 77780fec57..abd9b3d44f 100644 --- a/blocksuite/affine/shared/src/services/editor-setting-service.ts +++ b/blocksuite/affine/shared/src/services/editor-setting-service.ts @@ -10,7 +10,7 @@ export const GeneralSettingSchema = z .object({ edgelessScrollZoom: z.boolean().default(false), edgelessDisableScheduleUpdate: z.boolean().default(false), - docDropCanvasPreferView: z + docCanvasPreferView: z .enum(['affine:embed-linked-doc', 'affine:embed-synced-doc']) .default('affine:embed-synced-doc'), }) diff --git a/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/edgeless-embed-synced-doc-header.tsx b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/edgeless-embed-synced-doc-header.tsx index 3af1dd358a..96a118a857 100644 --- a/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/edgeless-embed-synced-doc-header.tsx +++ b/packages/frontend/core/src/blocksuite/extensions/edgeless-block-header/edgeless-embed-synced-doc-header.tsx @@ -70,6 +70,8 @@ const ToggleButton = ({ model }: { model: EmbedSyncedDocModel }) => { return ( { await openHomePage(page); await waitForEditorLoad(page); await clickNewPageButton(page, title); + await pressEnter(page); + await page.keyboard.type('test content'); await clickEdgelessModeButton(page); const container = locateEditorContainer(page); await container.click(); @@ -56,3 +61,43 @@ test('should not show hidden note in embed view page mode', async ({ await expect(embedLink.getByText(/visible content/)).toBeVisible(); await expect(embedLink.getByText(/hidden content/)).toBeHidden(); }); + +test.describe('edgeless', () => { + test.beforeEach(async ({ page }) => { + await clickNewPageButton(page); + await clickEdgelessModeButton(page); + await clickView(page, [0, 0]); + await page.keyboard.type('@' + title); + await page + .getByTestId('cmdk-quick-search') + .getByText(/^Synced Block Test$/) + .click(); + await fitViewportToContent(page); + }); + + test.describe('header of edgeless embed synced doc', () => { + test('should fold button works', async ({ page }) => { + const embedBlock = page.locator('affine-embed-edgeless-synced-doc-block'); + const foldButton = embedBlock.getByTestId( + 'edgeless-embed-synced-doc-fold-button' + ); + const content = embedBlock.locator('editor-host'); + + await expect(foldButton).toHaveAttribute('data-folded', 'false'); + await expect(content).toBeVisible(); + + await foldButton.click(); + + await expect(content).toBeHidden(); + await expect(foldButton).toHaveAttribute('data-folded', 'true'); + }); + + test('should show title in header', async ({ page }) => { + const embedBlock = page.locator('affine-embed-edgeless-synced-doc-block'); + const headerTitle = embedBlock.getByTestId( + 'edgeless-embed-synced-doc-title' + ); + await expect(headerTitle).toHaveText(title); + }); + }); +}); diff --git a/tests/affine-local/e2e/quick-search.spec.ts b/tests/affine-local/e2e/quick-search.spec.ts index 266e3060e1..fc40b7075a 100644 --- a/tests/affine-local/e2e/quick-search.spec.ts +++ b/tests/affine-local/e2e/quick-search.spec.ts @@ -484,21 +484,21 @@ test('can use @ to open quick search to search for doc and insert into canvas', // press enter to insert the page to canvas await page.keyboard.press('Enter'); await expect( - page.locator('affine-embed-edgeless-linked-doc-block') + page.locator('affine-embed-edgeless-synced-doc-block') ).toBeVisible(); await expect( - page.locator('.affine-embed-linked-doc-content-title') + page.getByTestId('edgeless-embed-synced-doc-title') ).toContainText(docTitle); // focus on the note block await page.waitForTimeout(500); await page - .locator('affine-embed-edgeless-linked-doc-block') + .locator('affine-embed-edgeless-synced-doc-block') .click({ force: true }); await page.waitForTimeout(500); // double clock to show peek view await page - .locator('affine-embed-edgeless-linked-doc-block') + .locator('affine-embed-edgeless-synced-doc-block') .dblclick({ force: true }); await expect(page.getByTestId('peek-view-modal')).toBeVisible(); }); diff --git a/tests/kit/src/utils/editor.ts b/tests/kit/src/utils/editor.ts index 5733812899..7cee473063 100644 --- a/tests/kit/src/utils/editor.ts +++ b/tests/kit/src/utils/editor.ts @@ -163,6 +163,17 @@ export async function setViewportZoom(page: Page, zoom = 1, editorIndex = 0) { }, zoom); } +export async function fitViewportToContent(page: Page, editorIndex = 0) { + const container = locateEditorContainer(page, editorIndex); + return container.evaluate(container => { + const root = container.querySelector('affine-edgeless-root'); + if (!root) { + throw new Error('Edgeless root not found'); + } + root.gfx.fitToScreen(); + }); +} + /** * Convert a canvas point to view coordinate * @param point the coordinate on the canvas