diff --git a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue
index 6a17e80c60f..f5611ef0693 100644
--- a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue
+++ b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputFull.vue
@@ -522,7 +522,14 @@ function removeOverride(clearField = false) {
@drop="onDrop"
>
-
+
diff --git a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue
index b314a9b15d3..ac57de1577e 100644
--- a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue
+++ b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ParameterInputOverrides/FromAiOverrideButton.vue
@@ -8,8 +8,9 @@ const i18n = useI18n();
withDefaults(
defineProps<{
position?: 'inline' | 'standalone';
+ size?: 'small' | 'medium';
}>(),
- { position: 'inline' },
+ { position: 'inline', size: 'medium' },
);
const emit = defineEmits<{
@@ -24,7 +25,7 @@ const emit = defineEmits<{
@@ -38,8 +39,8 @@ const emit = defineEmits<{
display: flex;
align-items: center;
justify-content: center;
- height: 30px;
- width: 30px;
+ height: var(--from-ai-button-size);
+ width: var(--from-ai-button-size);
background-color: var(--color--foreground);
color: var(--color--foreground--shade-2);
cursor: pointer;
@@ -55,6 +56,14 @@ const emit = defineEmits<{
}
}
+.small {
+ --from-ai-button-size: var(--height--sm);
+}
+
+.medium {
+ --from-ai-button-size: var(--height--md);
+}
+
.inline {
border-left: 0;
border-top-right-radius: var(--radius);
diff --git a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue
index bbcdd2ef264..6c21c6a0daa 100644
--- a/packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue
+++ b/packages/frontend/editor-ui/src/features/ndv/parameters/components/ResourceLocator/ResourceLocator.vue
@@ -1278,7 +1278,10 @@ function removeOverride() {
-
+
diff --git a/packages/quality/testing/playwright/pages/NodeDetailsViewPage.ts b/packages/quality/testing/playwright/pages/NodeDetailsViewPage.ts
index 6f1a94235e7..b87bba1f891 100644
--- a/packages/quality/testing/playwright/pages/NodeDetailsViewPage.ts
+++ b/packages/quality/testing/playwright/pages/NodeDetailsViewPage.ts
@@ -244,6 +244,22 @@ export class NodeDetailsViewPage extends BasePage {
return locatorByIndex(this.container.getByTestId(`parameter-input-${parameterName}`), index);
}
+ getParameterInputBorder(parameterName: string) {
+ return this.getParameterInput(parameterName).locator('.n8n-input__wrapper');
+ }
+
+ getParameterNumberBorder(parameterName: string) {
+ return this.getParameterInput(parameterName).getByTestId('input-number');
+ }
+
+ getParameterExpressionBorder(parameterName: string) {
+ return this.getParameterInput(parameterName).locator('.el-input-group__prepend').locator('..');
+ }
+
+ getFromAiOverrideButton(parameterName: string) {
+ return this.getParameterInput(parameterName).getByTestId('from-ai-override-button');
+ }
+
getParameterInputTextbox(parameterName: string, index?: number) {
return this.getParameterInput(parameterName, index).getByRole('textbox');
}
diff --git a/packages/quality/testing/playwright/tests/e2e/regression/ado-5599-from-ai-button-alignment.spec.ts b/packages/quality/testing/playwright/tests/e2e/regression/ado-5599-from-ai-button-alignment.spec.ts
new file mode 100644
index 00000000000..9cff0148469
--- /dev/null
+++ b/packages/quality/testing/playwright/tests/e2e/regression/ado-5599-from-ai-button-alignment.spec.ts
@@ -0,0 +1,51 @@
+import { test, expect } from '../../../fixtures/base';
+import type { Locator } from '@playwright/test';
+
+async function expectButtonAligned(inputBorder: Locator, fromAiButton: Locator) {
+ await expect(inputBorder).toBeVisible();
+ await expect(fromAiButton).toBeVisible();
+
+ const inputBounds = await inputBorder.boundingBox();
+ const buttonBounds = await fromAiButton.boundingBox();
+ expect(inputBounds).not.toBeNull();
+ expect(buttonBounds).not.toBeNull();
+
+ expect(buttonBounds).toMatchObject({
+ y: inputBounds!.y,
+ height: inputBounds!.height,
+ });
+}
+
+test.describe(
+ 'ADO-5599: From AI button alignment',
+ { annotation: [{ type: 'owner', description: 'Adore' }] },
+ () => {
+ test('aligns the From AI button outline with the tool parameter input border', async ({
+ n8n,
+ }) => {
+ await n8n.start.fromBlankCanvas();
+ await n8n.canvas.addNode('AI Agent', { closeNDV: true });
+ await n8n.canvas.getInputPlusEndpointByType('AI Agent', 'ai_tool').click();
+ await n8n.canvas.nodeCreator.searchFor('SearXNG');
+ await n8n.canvas.nodeCreator.selectItem('SearXNG');
+ await n8n.ndv.addParameterOptionByName('Language');
+
+ await expectButtonAligned(
+ n8n.ndv.getParameterInputBorder('language'),
+ n8n.ndv.getFromAiOverrideButton('language'),
+ );
+
+ await n8n.ndv.addParameterOptionByName('Number of Results');
+ await expectButtonAligned(
+ n8n.ndv.getParameterNumberBorder('numResults'),
+ n8n.ndv.getFromAiOverrideButton('numResults'),
+ );
+
+ await n8n.ndv.setParameterInput('language', '={{ $json.language }}');
+ await expectButtonAligned(
+ n8n.ndv.getParameterExpressionBorder('language'),
+ n8n.ndv.getFromAiOverrideButton('language'),
+ );
+ });
+ },
+);