mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-12 02:19:43 +00:00
chore(editor): Clean up AI workflow builder header (#26037)
Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
71194488aa
commit
0c29bb038f
+10
-16
@@ -11,7 +11,7 @@ import AssistantIcon from '../AskAssistantIcon/AssistantIcon.vue';
|
||||
import AssistantText from '../AskAssistantText/AssistantText.vue';
|
||||
import InlineAskAssistantButton from '../InlineAskAssistantButton/InlineAskAssistantButton.vue';
|
||||
import N8nButton from '../N8nButton';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
import N8nIconButton from '../N8nIconButton';
|
||||
import N8nPromptInput from '../N8nPromptInput';
|
||||
import N8nPromptInputSuggestions from '../N8nPromptInputSuggestions';
|
||||
import N8nScrollArea from '../N8nScrollArea/N8nScrollArea.vue';
|
||||
@@ -465,13 +465,16 @@ defineExpose({
|
||||
<AssistantIcon size="large" />
|
||||
<AssistantText size="large" :text="t('assistantChat.aiAssistantLabel')" />
|
||||
</div>
|
||||
<span :class="$style.betaTag">{{ t('assistantChat.aiAssistantBetaLabel') }}</span>
|
||||
</div>
|
||||
<slot name="header" />
|
||||
</div>
|
||||
<div :class="$style.back" data-test-id="close-chat-button" @click="onClose">
|
||||
<N8nIcon icon="arrow-right" color="text-base" />
|
||||
</div>
|
||||
<N8nIconButton
|
||||
icon="x"
|
||||
variant="ghost"
|
||||
size="large"
|
||||
data-test-id="close-chat-button"
|
||||
@click="onClose"
|
||||
/>
|
||||
</div>
|
||||
<div :class="$style.body">
|
||||
<div v-if="normalizedMessages?.length || loadingMessage" :class="$style.messages">
|
||||
@@ -708,11 +711,12 @@ defineExpose({
|
||||
|
||||
.header {
|
||||
height: 65px; // same as header height in editor
|
||||
padding: 0 var(--spacing--lg);
|
||||
padding: 0 var(--spacing--sm) 0 var(--spacing--lg);
|
||||
background-color: var(--color--background--light-3);
|
||||
border: var(--border);
|
||||
border-top: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
@@ -724,12 +728,6 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.betaTag {
|
||||
color: var(--color--text);
|
||||
font-size: var(--font-size--2xs);
|
||||
font-weight: var(--font-weight--bold);
|
||||
}
|
||||
|
||||
.body {
|
||||
background-color: var(--color--background--light-2);
|
||||
border: var(--border);
|
||||
@@ -830,10 +828,6 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.back:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.quickReplies {
|
||||
margin-top: var(--spacing--sm);
|
||||
|
||||
|
||||
+63
-98
@@ -26,25 +26,20 @@ exports[`AskAssistantChat > limits maximum input length when maxCharacterLength
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -131,25 +126,20 @@ exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -379,25 +369,20 @@ exports[`AskAssistantChat > renders default placeholder chat correctly 1`] = `
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -484,25 +469,20 @@ exports[`AskAssistantChat > renders end of session chat correctly 1`] = `
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -641,25 +621,20 @@ exports[`AskAssistantChat > renders error message correctly with retry button 1`
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -779,25 +754,20 @@ exports[`AskAssistantChat > renders message with code snippet 1`] = `
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
@@ -917,25 +887,20 @@ exports[`AskAssistantChat > renders streaming chat correctly 1`] = `
|
||||
text="assistantChat.aiAssistantLabel"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="betaTag"
|
||||
>
|
||||
assistantChat.aiAssistantBetaLabel
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div
|
||||
class="back"
|
||||
<n8n-icon-button-stub
|
||||
class=""
|
||||
data-test-id="close-chat-button"
|
||||
>
|
||||
<n8n-icon-stub
|
||||
color="text-base"
|
||||
icon="arrow-right"
|
||||
spin="false"
|
||||
/>
|
||||
</div>
|
||||
disabled="false"
|
||||
icon="x"
|
||||
icononly="true"
|
||||
loading="false"
|
||||
size="large"
|
||||
variant="ghost"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="body"
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
"aiAssistant.assistant": "Assistant",
|
||||
"aiAssistant.tabs.ask": "Ask",
|
||||
"aiAssistant.tabs.build": "Build",
|
||||
"aiAssistant.reducedHelp.chat.notice": "You have opted not to share actual data values. As a result, AI responses will be less accurate and context-aware.",
|
||||
"aiAssistant.reducedHelp.chat.notice": "Your admin turned off sending actual data values to the AI. Responses may be less accurate.",
|
||||
"aiAssistant.tabs.builder.disabled.tooltip": "AI Builder is disabled because sending data values to AI is turned off. Enable it in AI Usage settings to use AI Builder.",
|
||||
"aiAssistant.builder.mode": "AI Builder",
|
||||
"aiAssistant.builder.placeholder": "Ask n8n to build...",
|
||||
|
||||
+10
-29
@@ -30,13 +30,12 @@ import { canvasEventBus } from '@/features/workflows/canvas/canvas.eventBus';
|
||||
import { useErrorHandler } from '@/app/composables/useErrorHandler';
|
||||
import type { WorkflowDataUpdate } from '@n8n/rest-api-client/api/workflows';
|
||||
import shuffle from 'lodash/shuffle';
|
||||
import AISettingsButton from '@/features/ai/assistant/components/Chat/AISettingsButton.vue';
|
||||
import { useAssistantStore } from '@/features/ai/assistant/assistant.store';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import { useChatPanelStateStore } from '@/features/ai/assistant/chatPanelState.store';
|
||||
import { useReviewChanges } from '@/features/ai/assistant/composables/useReviewChanges';
|
||||
|
||||
import { N8nAskAssistantChat } from '@n8n/design-system';
|
||||
import { N8nAskAssistantChat, N8nInfoTip } from '@n8n/design-system';
|
||||
import BuildModeEmptyState from './BuildModeEmptyState.vue';
|
||||
import {
|
||||
isPlanModePlanMessage,
|
||||
@@ -59,11 +58,11 @@ const workflowHistoryStore = useWorkflowHistoryStore();
|
||||
const historyStore = useHistoryStore();
|
||||
const collaborationStore = useCollaborationStore();
|
||||
const workflowAutosaveStore = useWorkflowSaveStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const telemetry = useTelemetry();
|
||||
const slots = useSlots();
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const assistantStore = useAssistantStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const chatPanelStateStore = useChatPanelStateStore();
|
||||
const router = useRouter();
|
||||
const i18n = useI18n();
|
||||
@@ -101,12 +100,9 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
const showSettingsButton = computed(() => {
|
||||
return assistantStore.canManageAISettings;
|
||||
});
|
||||
|
||||
const allowSendingParameterValues = computed(
|
||||
() => settingsStore.settings.ai.allowSendingParameterValues,
|
||||
const showUsabilityNotice = computed(
|
||||
() =>
|
||||
assistantStore.canManageAISettings && !settingsStore.settings.ai.allowSendingParameterValues,
|
||||
);
|
||||
|
||||
const shouldShowNotificationBanner = computed(() => {
|
||||
@@ -532,15 +528,11 @@ defineExpose({
|
||||
<template #focused-nodes-chips="{ message }">
|
||||
<MessageFocusedNodesChips :focused-node-names="message.focusedNodeNames" />
|
||||
</template>
|
||||
<template #header>
|
||||
<div :class="{ [$style.header]: true, [$style['with-slot']]: !!slots.header }">
|
||||
<slot name="header" />
|
||||
<AISettingsButton
|
||||
v-if="showSettingsButton"
|
||||
:show-usability-notice="!allowSendingParameterValues"
|
||||
:disabled="builderStore.streaming"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="slots.header || showUsabilityNotice" #header>
|
||||
<slot name="header" />
|
||||
<N8nInfoTip v-if="showUsabilityNotice" theme="warning" type="tooltip">
|
||||
<span>{{ i18n.baseText('aiAssistant.reducedHelp.chat.notice') }}</span>
|
||||
</N8nInfoTip>
|
||||
</template>
|
||||
<template #inputHeader>
|
||||
<ReviewChangesBanner
|
||||
@@ -667,17 +659,6 @@ defineExpose({
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
&.with-slot {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.newWorkflowButtonWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { N8nLink, N8nTooltip, N8nIcon, N8nInfoTip } from '@n8n/design-system';
|
||||
import { VIEWS } from '@/app/constants';
|
||||
|
||||
type Props = {
|
||||
disabled?: boolean;
|
||||
showUsabilityNotice?: boolean;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
disabled: false,
|
||||
showUsabilityNotice: false,
|
||||
});
|
||||
|
||||
const i18n = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<div :class="$style.container" data-test-id="ai-settings-button">
|
||||
<N8nInfoTip v-if="props.showUsabilityNotice" theme="warning" type="tooltip">
|
||||
<span>{{ i18n.baseText('aiAssistant.reducedHelp.chat.notice') }}</span>
|
||||
</N8nInfoTip>
|
||||
<N8nTooltip :content="i18n.baseText('settings.ai.button.tooltip')" :disabled="props.disabled">
|
||||
<N8nLink
|
||||
:to="props.disabled ? undefined : { name: VIEWS.AI_SETTINGS }"
|
||||
:aria-label="i18n.baseText('settings.ai.button.tooltip')"
|
||||
:class="{ [$style.link]: true, [$style.disabled]: props.disabled }"
|
||||
>
|
||||
<N8nIcon icon="settings" size="large" color="text-light" />
|
||||
</N8nLink>
|
||||
</N8nTooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing--2xs);
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: var(--spacing--4xs);
|
||||
|
||||
svg {
|
||||
color: var(--color--text);
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
color: var(--color--text--shade-1);
|
||||
}
|
||||
|
||||
:global(.n8n-text) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
svg {
|
||||
color: var(--color--text--tint-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+12
-31
@@ -1,13 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { useAssistantStore } from '@/features/ai/assistant/assistant.store';
|
||||
import { useUsersStore } from '@/features/settings/users/users.store';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import { computed, ref, useSlots } from 'vue';
|
||||
import { N8nAskAssistantChat } from '@n8n/design-system';
|
||||
import AISettingsButton from '@/features/ai/assistant/components/Chat/AISettingsButton.vue';
|
||||
import { N8nAskAssistantChat, N8nInfoTip } from '@n8n/design-system';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
import { injectWorkflowState } from '@/app/composables/useWorkflowState';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import AskModeEmptyState from './AskModeEmptyState.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -15,19 +14,15 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const assistantStore = useAssistantStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const workflowState = injectWorkflowState();
|
||||
const usersStore = useUsersStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const telemetry = useTelemetry();
|
||||
const slots = useSlots();
|
||||
const i18n = useI18n();
|
||||
|
||||
const n8nChatRef = ref<InstanceType<typeof N8nAskAssistantChat>>();
|
||||
|
||||
const allowSendingParameterValues = computed(
|
||||
() => settingsStore.settings.ai.allowSendingParameterValues,
|
||||
);
|
||||
|
||||
const user = computed(() => ({
|
||||
firstName: usersStore.currentUser?.firstName ?? '',
|
||||
lastName: usersStore.currentUser?.lastName ?? '',
|
||||
@@ -35,9 +30,10 @@ const user = computed(() => ({
|
||||
|
||||
const loadingMessage = computed(() => assistantStore.assistantThinkingMessage);
|
||||
|
||||
const showSettingsButton = computed(() => {
|
||||
return assistantStore.canManageAISettings;
|
||||
});
|
||||
const showUsabilityNotice = computed(
|
||||
() =>
|
||||
assistantStore.canManageAISettings && !settingsStore.settings.ai.allowSendingParameterValues,
|
||||
);
|
||||
|
||||
async function onUserMessage(content: string, quickReplyType?: string, isFeedback = false) {
|
||||
// If there is no current session running, initialize the support chat session
|
||||
@@ -99,15 +95,11 @@ defineExpose({
|
||||
@code-replace="onCodeReplace"
|
||||
@code-undo="undoCodeDiff"
|
||||
>
|
||||
<template #header>
|
||||
<div :class="{ [$style.header]: true, [$style['with-slot']]: !!slots.header }">
|
||||
<slot name="header" />
|
||||
<AISettingsButton
|
||||
v-if="showSettingsButton"
|
||||
:show-usability-notice="!allowSendingParameterValues"
|
||||
:disabled="assistantStore.streaming"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="slots.header || showUsabilityNotice" #header>
|
||||
<slot name="header" />
|
||||
<N8nInfoTip v-if="showUsabilityNotice" theme="warning" type="tooltip">
|
||||
<span>{{ i18n.baseText('aiAssistant.reducedHelp.chat.notice') }}</span>
|
||||
</N8nInfoTip>
|
||||
</template>
|
||||
<template #placeholder>
|
||||
<AskModeEmptyState />
|
||||
@@ -121,15 +113,4 @@ defineExpose({
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
|
||||
&.with-slot {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user