From d6370a0002e75487200318bd739de25cccd24259 Mon Sep 17 00:00:00 2001 From: Jaakko Husso Date: Fri, 9 Oct 2026 15:09:42 +0000 Subject: [PATCH] fix(editor): Show one attach button in the agent chat composer (no-changelog) (#40810) Co-authored-by: Claude Opus 5.5 --- .../src/features/agents/__tests__/AgentChatPanel.test.ts | 7 +++++++ .../features/ai/shared/components/ChatInputBase.test.ts | 8 ++++++++ .../src/features/ai/shared/components/ChatInputBase.vue | 8 +++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/frontend/editor-ui/src/features/agents/__tests__/AgentChatPanel.test.ts b/packages/frontend/editor-ui/src/features/agents/__tests__/AgentChatPanel.test.ts index 8a4487ac05d..946f47b68aa 100644 --- a/packages/frontend/editor-ui/src/features/agents/__tests__/AgentChatPanel.test.ts +++ b/packages/frontend/editor-ui/src/features/agents/__tests__/AgentChatPanel.test.ts @@ -225,6 +225,7 @@ vi.mock('@/features/ai/shared/components/ChatInputBase.vue', async () => { 'disabled', 'maxLength', 'showAttach', + 'showAttachButton', 'acceptedMimeTypes', ], emits: ['submit', 'stop', 'update:modelValue', 'files-selected'], @@ -1040,6 +1041,12 @@ describe('AgentChatPanel', () => { expect(wrapper.find('[data-test-id="chat-input-attach-button"]').exists()).toBe(true); }); + it('hides the built-in composer attach button in favor of its own footer button', () => { + const wrapper = mountPanel(); + + expect(wrapper.findComponent({ name: 'ChatInputBase' }).props('showAttachButton')).toBe(false); + }); + it('hides the attach button when the model accepts no attachments', () => { const wrapper = mountPanel({ agentConfig: { ...defaultAgentConfig, model: 'unknown-provider/model' }, diff --git a/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.test.ts b/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.test.ts index f418006fbde..3542ea5b033 100644 --- a/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.test.ts +++ b/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.test.ts @@ -190,6 +190,14 @@ describe('ChatInputBase', () => { expect(queryByTestId('chat-input-attach-button')).not.toBeInTheDocument(); }); + it('hides the built-in attach button when showAttachButton is false', () => { + const { queryByTestId } = renderComponent({ + props: makeProps({ showAttach: true, showAttachButton: false, acceptedMimeTypes: 'image/*' }), + }); + + expect(queryByTestId('chat-input-attach-button')).not.toBeInTheDocument(); + }); + it('shows the drop overlay and emits dropped files', async () => { const file = new File(['image'], 'image.png', { type: 'image/png' }); const { getByRole, getByTestId, queryByTestId, emitted } = renderComponent({ diff --git a/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue b/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue index 462ed2d2b14..a70da899498 100644 --- a/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue +++ b/packages/frontend/editor-ui/src/features/ai/shared/components/ChatInputBase.vue @@ -25,6 +25,11 @@ const props = withDefaults( disabled?: boolean; showVoice?: boolean; showAttach?: boolean; + /** + * Shows the built-in attach button. A host that renders its own attach + * button sets this to false. Files can still be dropped or pasted. + */ + showAttachButton?: boolean; /** Also shows the attach button. Without it, files can still be dropped or pasted. */ acceptedMimeTypes?: string; /** @@ -46,6 +51,7 @@ const props = withDefaults( { placeholder: undefined, showStopButton: undefined, + showAttachButton: true, acceptedMimeTypes: undefined, attachedEncodedBytes: 0, autosize: () => ({ minRows: 2, maxRows: 6 }), @@ -294,7 +300,7 @@ defineExpose({