diff --git a/blocksuite/framework/std/src/inline/range/active.ts b/blocksuite/framework/std/src/inline/range/active.ts index 8afbea1795..62a40a0426 100644 --- a/blocksuite/framework/std/src/inline/range/active.ts +++ b/blocksuite/framework/std/src/inline/range/active.ts @@ -1,3 +1,5 @@ +import { RANGE_SYNC_EXCLUDE_ATTR } from './consts'; + /** * Check if the active element is in the editor host. * TODO(@mirone): this is a trade-off, we need to use separate awareness store for every store to make sure the selection is isolated. @@ -8,6 +10,9 @@ export function isActiveInEditor(editorHost: HTMLElement) { const currentActiveElement = document.activeElement; if (!currentActiveElement) return false; + // The input or textarea in the widget should be ignored. + if (currentActiveElement.closest(`[${RANGE_SYNC_EXCLUDE_ATTR}="true"]`)) + return false; const currentEditorHost = currentActiveElement?.closest('editor-host'); if (!currentEditorHost) return false; return currentEditorHost === editorHost; diff --git a/blocksuite/framework/std/src/inline/range/range-binding.ts b/blocksuite/framework/std/src/inline/range/range-binding.ts index 0c4f808ca6..e154031e95 100644 --- a/blocksuite/framework/std/src/inline/range/range-binding.ts +++ b/blocksuite/framework/std/src/inline/range/range-binding.ts @@ -210,12 +210,11 @@ export class RangeBinding { const el = getElement(range.commonAncestorContainer); if (!el) return; - const closestExclude = el.closest(`[${RANGE_SYNC_EXCLUDE_ATTR}]`); - if (closestExclude?.getAttribute(RANGE_SYNC_EXCLUDE_ATTR) === 'true') - return; + const closestExclude = el.closest(`[${RANGE_SYNC_EXCLUDE_ATTR}="true"]`); + if (closestExclude) return; - const closestEditable = el.closest('[contenteditable]'); - if (closestEditable?.getAttribute('contenteditable') === 'false') return; + const closestEditable = el.closest('[contenteditable="false"]'); + if (closestEditable) return; const startElement = getElement(range.startContainer); const endElement = getElement(range.endContainer); diff --git a/packages/frontend/core/src/blocksuite/ai/widgets/ai-panel/ai-panel.ts b/packages/frontend/core/src/blocksuite/ai/widgets/ai-panel/ai-panel.ts index 55b745b556..70de65ae47 100644 --- a/packages/frontend/core/src/blocksuite/ai/widgets/ai-panel/ai-panel.ts +++ b/packages/frontend/core/src/blocksuite/ai/widgets/ai-panel/ai-panel.ts @@ -15,6 +15,7 @@ import { } from '@blocksuite/affine/shared/utils'; import { WidgetComponent, WidgetViewExtension } from '@blocksuite/affine/std'; import { GfxControllerIdentifier } from '@blocksuite/affine/std/gfx'; +import { RANGE_SYNC_EXCLUDE_ATTR } from '@blocksuite/affine/std/inline'; import type { BaseSelection } from '@blocksuite/affine/store'; import { autoPlacement, @@ -415,6 +416,8 @@ export class AffineAIPanelWidget extends WidgetComponent { super.connectedCallback(); this.tabIndex = -1; + // No need to synchronize the contents of the input into the editor. + this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true'); this.disposables.addFromEvent( document, 'pointerdown', @@ -437,6 +440,9 @@ export class AffineAIPanelWidget extends WidgetComponent { this.disposables.addFromEvent(this, 'wheel', stopPropagation); this.disposables.addFromEvent(this, 'pointerdown', stopPropagation); this.disposables.addFromEvent(this, 'pointerup', stopPropagation); + this.disposables.addFromEvent(this, 'cut', stopPropagation); + this.disposables.addFromEvent(this, 'copy', stopPropagation); + this.disposables.addFromEvent(this, 'paste', stopPropagation); this.disposables.addFromEvent(this, 'keydown', this._onKeyDown); } diff --git a/tests/affine-cloud-copilot/e2e/chat-with/text.spec.ts b/tests/affine-cloud-copilot/e2e/chat-with/text.spec.ts index 11396c8f83..3bbb9ce702 100644 --- a/tests/affine-cloud-copilot/e2e/chat-with/text.spec.ts +++ b/tests/affine-cloud-copilot/e2e/chat-with/text.spec.ts @@ -127,4 +127,15 @@ test.describe('AIChatWith/Text', () => { const content = await utils.editor.getEditorContent(page); expect(content).toBe('Apple'); }); + + test('should focus on textarea', async ({ page, utils }) => { + await utils.editor.askAIWithText(page, 'Apple'); + + const textarea = await utils.editor.whatAreYourThoughts(page, 'Coffee'); + + await expect(textarea).toBeFocused(); + + const value = await textarea.inputValue(); + expect(value).toBe('Coffee'); + }); }); diff --git a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts index 14c47721ab..3d46057e86 100644 --- a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts +++ b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts @@ -582,4 +582,12 @@ export class EditorUtils { ), } as const; } + + public static async whatAreYourThoughts(page: Page, text: string) { + const textarea = page.locator( + 'affine-ai-panel-widget .ai-panel-container textarea' + ); + await textarea.fill(text); + return textarea; + } }