diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts
index dbc613bd64..8ce3bc4080 100644
--- a/cypress/e2e/2-credentials.cy.ts
+++ b/cypress/e2e/2-credentials.cy.ts
@@ -15,7 +15,7 @@ import {
TRELLO_NODE_NAME,
} from '../constants';
import { CredentialsModal, CredentialsPage, NDV, WorkflowPage } from '../pages';
-import { successToast } from '../pages/notifications';
+import { errorToast, successToast } from '../pages/notifications';
import { getVisibleSelect } from '../utils';
const credentialsPage = new CredentialsPage();
@@ -278,4 +278,25 @@ describe('Credentials', () => {
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
nodeDetailsView.getters.copyInput().should('not.exist');
});
+
+ it('ADO-2583 should show notifications above credential modal overlay', () => {
+ // check error notifications because they are sticky
+ cy.intercept('POST', '/rest/credentials', { forceNetworkError: true });
+ credentialsPage.getters.createCredentialButton().click();
+
+ credentialsModal.getters.newCredentialModal().should('be.visible');
+ credentialsModal.getters.newCredentialTypeSelect().should('be.visible');
+ credentialsModal.getters.newCredentialTypeOption('Notion API').click();
+
+ credentialsModal.getters.newCredentialTypeButton().click();
+ credentialsModal.getters.connectionParameter('Internal Integration Secret').type('1234567890');
+
+ credentialsModal.actions.setName('My awesome Notion account');
+ credentialsModal.getters.saveButton().click({ force: true });
+ errorToast().should('have.length', 1);
+ errorToast().should('be.visible');
+
+ errorToast().should('have.css', 'z-index', '2100');
+ cy.get('.el-overlay').should('have.css', 'z-index', '2001');
+ });
});
diff --git a/packages/cli/src/runners/task-runner-server.ts b/packages/cli/src/runners/task-runner-server.ts
index 5baaf9fc90..2199e70b38 100644
--- a/packages/cli/src/runners/task-runner-server.ts
+++ b/packages/cli/src/runners/task-runner-server.ts
@@ -125,11 +125,13 @@ export class TaskRunnerServer {
const { app } = this;
// Augment errors sent to Sentry
- const {
- Handlers: { requestHandler, errorHandler },
- } = await import('@sentry/node');
- app.use(requestHandler());
- app.use(errorHandler());
+ if (this.globalConfig.sentry.backendDsn) {
+ const {
+ Handlers: { requestHandler, errorHandler },
+ } = await import('@sentry/node');
+ app.use(requestHandler());
+ app.use(errorHandler());
+ }
}
private setupCommonMiddlewares() {
diff --git a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
index 1acffa82fa..78b4ccbf51 100644
--- a/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
+++ b/packages/design-system/src/components/AskAssistantChat/AskAssistantChat.vue
@@ -226,6 +226,16 @@ async function onCopyButtonClick(content: string, e: MouseEvent) {
data-test-id="chat-message-system"
>
⚠️ {{ message.content }}
+ message.retry?.()"
+ >
+ {{ t('generic.retry') }}
+
{
});
expect(container).toMatchSnapshot();
});
+
+ it('renders error message correctly with retry button', () => {
+ const wrapper = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ stubs,
+ },
+ props: {
+ user: { firstName: 'Kobi', lastName: 'Dog' },
+ messages: [
+ {
+ id: '1',
+ role: 'assistant',
+ type: 'error',
+ content: 'This is an error message.',
+ read: false,
+ // Button is not shown without a retry function
+ retry: async () => {},
+ },
+ ],
+ },
+ });
+ expect(wrapper.container).toMatchSnapshot();
+ expect(wrapper.getByTestId('error-retry-button')).toBeInTheDocument();
+ });
+
+ it('does not render retry button if no error is present', () => {
+ const wrapper = render(AskAssistantChat, {
+ global: {
+ directives: {
+ n8nHtml,
+ },
+ stubs,
+ },
+ props: {
+ user: { firstName: 'Kobi', lastName: 'Dog' },
+ messages: [
+ {
+ id: '1',
+ type: 'text',
+ role: 'assistant',
+ content:
+ 'Hi Max! Here is my top solution to fix the error in your **Transform data** node👇',
+ read: false,
+ },
+ ],
+ },
+ });
+
+ expect(wrapper.container).toMatchSnapshot();
+ expect(wrapper.queryByTestId('error-retry-button')).not.toBeInTheDocument();
+ });
});
diff --git a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
index 891c10abf6..c26913e405 100644
--- a/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
+++ b/packages/design-system/src/components/AskAssistantChat/__tests__/__snapshots__/AskAssistantChat.spec.ts.snap
@@ -1,5 +1,179 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+exports[`AskAssistantChat > does not render retry button if no error is present 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Hi Max! Here is my top solution to fix the error in your
+
+ Transform data
+
+ node👇
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
exports[`AskAssistantChat > renders chat with messages correctly 1`] = `
renders end of session chat correctly 1`] = `
`;
+exports[`AskAssistantChat > renders error message correctly with retry button 1`] = `
+
+
+
+
+
+
+
+
+
+
+ ⚠️ This is an error message.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
exports[`AskAssistantChat > renders message with code snippet 1`] = `
Promise
;
}
export interface AgentSuggestionMessage {
diff --git a/packages/editor-ui/src/App.vue b/packages/editor-ui/src/App.vue
index 4c849fb5d9..77d371855c 100644
--- a/packages/editor-ui/src/App.vue
+++ b/packages/editor-ui/src/App.vue
@@ -16,6 +16,7 @@ import { useUIStore } from '@/stores/ui.store';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useHistoryHelper } from '@/composables/useHistoryHelper';
+import { useStyles } from './composables/useStyles';
const route = useRoute();
const rootStore = useRootStore();
@@ -24,6 +25,8 @@ const uiStore = useUIStore();
const usersStore = useUsersStore();
const settingsStore = useSettingsStore();
+const { setAppZIndexes } = useStyles();
+
// Initialize undo/redo
useHistoryHelper(route);
@@ -41,6 +44,7 @@ watch(defaultLocale, (newLocale) => {
});
onMounted(async () => {
+ setAppZIndexes();
logHiringBanner();
void useExternalHooks().run('app.mount');
loading.value = false;
@@ -134,7 +138,7 @@ const updateGridWidth = async () => {
.banners {
grid-area: banners;
- z-index: 999;
+ z-index: var(--z-index-top-banners);
}
.content {
@@ -154,13 +158,13 @@ const updateGridWidth = async () => {
.header {
grid-area: header;
- z-index: 99;
+ z-index: var(--z-index-app-header);
}
.sidebar {
grid-area: sidebar;
height: 100%;
- z-index: 999;
+ z-index: var(--z-index-app-sidebar);
}
.modals {
diff --git a/packages/editor-ui/src/components/AskAssistant/AskAssistantChat.vue b/packages/editor-ui/src/components/AskAssistant/AskAssistantChat.vue
index bef68236f8..db4d8ee105 100644
--- a/packages/editor-ui/src/components/AskAssistant/AskAssistantChat.vue
+++ b/packages/editor-ui/src/components/AskAssistant/AskAssistantChat.vue
@@ -106,7 +106,7 @@ function onClose() {
.container {
height: 100%;
flex-basis: content;
- z-index: 300;
+ z-index: var(--z-index-ask-assistant-chat);
}
.wrapper {
diff --git a/packages/editor-ui/src/components/AskAssistant/AskAssistantFloatingButton.vue b/packages/editor-ui/src/components/AskAssistant/AskAssistantFloatingButton.vue
index b147ecca9f..70f6ed84fd 100644
--- a/packages/editor-ui/src/components/AskAssistant/AskAssistantFloatingButton.vue
+++ b/packages/editor-ui/src/components/AskAssistant/AskAssistantFloatingButton.vue
@@ -1,5 +1,6 @@
-
-
- $emit('click')"
- @mouseover="showTooltip = true"
- @mouseleave="showTooltip = false"
- >
-
-
-
-
-
![]()
-
-
-
- {{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
-
-
-
-
- {{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
-
-
-
-
-
diff --git a/packages/editor-ui/src/components/Modal.vue b/packages/editor-ui/src/components/Modal.vue
index 3877b8dc53..3f98e2a023 100644
--- a/packages/editor-ui/src/components/Modal.vue
+++ b/packages/editor-ui/src/components/Modal.vue
@@ -5,6 +5,7 @@ import type { EventBus } from 'n8n-design-system';
import { useUIStore } from '@/stores/ui.store';
import type { ModalKey } from '@/Interface';
import { APP_MODALS_ELEMENT_ID } from '@/constants';
+import { useStyles } from '@/composables/useStyles';
const props = withDefaults(
defineProps<{
@@ -50,6 +51,8 @@ const props = withDefaults(
const emit = defineEmits<{ enter: [] }>();
+const { APP_Z_INDEXES } = useStyles();
+
const styles = computed(() => {
const styles: { [prop: string]: string } = {};
if (props.height) {
@@ -143,7 +146,7 @@ function getCustomClass() {
:append-to-body="appendToBody"
:data-test-id="`${name}-modal`"
:modal-class="center ? $style.center : ''"
- :z-index="2000"
+ :z-index="APP_Z_INDEXES.MODALS"
>
diff --git a/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue b/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
index d93e83ebfa..82767f964d 100644
--- a/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
+++ b/packages/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
@@ -173,7 +173,7 @@ onBeforeUnmount(() => {
top: $header-height;
bottom: 0;
right: 0;
- z-index: 200;
+ z-index: var(--z-index-node-creator);
width: $node-creator-width;
color: $node-creator-text-color;
}
diff --git a/packages/editor-ui/src/components/NodeDetailsView.vue b/packages/editor-ui/src/components/NodeDetailsView.vue
index f66800e384..8294ff2f24 100644
--- a/packages/editor-ui/src/components/NodeDetailsView.vue
+++ b/packages/editor-ui/src/components/NodeDetailsView.vue
@@ -36,6 +36,7 @@ import { usePinnedData } from '@/composables/usePinnedData';
import { useTelemetry } from '@/composables/useTelemetry';
import { useI18n } from '@/composables/useI18n';
import { storeToRefs } from 'pinia';
+import { useStyles } from '@/composables/useStyles';
const emit = defineEmits<{
saveKeyboardShortcut: [event: KeyboardEvent];
@@ -73,6 +74,7 @@ const deviceSupport = useDeviceSupport();
const telemetry = useTelemetry();
const i18n = useI18n();
const message = useMessage();
+const { APP_Z_INDEXES } = useStyles();
const settingsEventBus = createEventBus();
const redrawRequired = ref(false);
@@ -668,7 +670,7 @@ onBeforeUnmount(() => {
width="auto"
:append-to="`#${APP_MODALS_ELEMENT_ID}`"
data-test-id="ndv"
- :z-index="1800"
+ :z-index="APP_Z_INDEXES.NDV"
:data-has-output-connection="hasOutputConnection"
>
(false);
const isTouchActive = ref(false);
@@ -136,7 +138,9 @@ const stickySize = computed(() => ({
const stickyPosition = computed(() => ({
left: position.value[0] + 'px',
top: position.value[1] + 'px',
- zIndex: props.isActive ? 9999999 : -1 * Math.floor((height.value * width.value) / 1000),
+ zIndex: props.isActive
+ ? APP_Z_INDEXES.ACTIVE_STICKY
+ : -1 * Math.floor((height.value * width.value) / 1000),
}));
const workflowRunning = computed(() => uiStore.isActionActive.workflowRunning);
diff --git a/packages/editor-ui/src/components/WorkflowPreview.vue b/packages/editor-ui/src/components/WorkflowPreview.vue
index 4f295b924d..ff617499ff 100644
--- a/packages/editor-ui/src/components/WorkflowPreview.vue
+++ b/packages/editor-ui/src/components/WorkflowPreview.vue
@@ -242,7 +242,7 @@ watch(
left: 0;
height: 100%;
width: 100%;
- z-index: 9999999;
+ z-index: var(--z-index-workflow-preview-ndv);
}
.spinner {
diff --git a/packages/editor-ui/src/composables/useCanvasMouseSelect.ts b/packages/editor-ui/src/composables/useCanvasMouseSelect.ts
index 6beaf33d60..83c1edbe42 100644
--- a/packages/editor-ui/src/composables/useCanvasMouseSelect.ts
+++ b/packages/editor-ui/src/composables/useCanvasMouseSelect.ts
@@ -7,6 +7,7 @@ import { getMousePosition, getRelativePosition } from '@/utils/nodeViewUtils';
import { ref, computed } from 'vue';
import { useCanvasStore } from '@/stores/canvas.store';
import { useContextMenu } from './useContextMenu';
+import { useStyles } from './useStyles';
interface ExtendedHTMLSpanElement extends HTMLSpanElement {
x: number;
@@ -22,6 +23,7 @@ export default function useCanvasMouseSelect() {
const canvasStore = useCanvasStore();
const workflowsStore = useWorkflowsStore();
const { isOpen: isContextMenuOpen } = useContextMenu();
+ const { APP_Z_INDEXES } = useStyles();
function _setSelectBoxStyle(styles: Record) {
Object.assign(selectBox.value.style, styles);
@@ -106,7 +108,7 @@ export default function useCanvasMouseSelect() {
border: '2px dotted #FF0000',
// Positioned absolutely within #node-view. This is consistent with how nodes are positioned.
position: 'absolute',
- zIndex: '100',
+ zIndex: `${APP_Z_INDEXES.SELECT_BOX}`,
visibility: 'hidden',
});
diff --git a/packages/editor-ui/src/composables/useStyles.spec.ts b/packages/editor-ui/src/composables/useStyles.spec.ts
new file mode 100644
index 0000000000..2e27979b3e
--- /dev/null
+++ b/packages/editor-ui/src/composables/useStyles.spec.ts
@@ -0,0 +1,25 @@
+import { useStyles } from './useStyles';
+
+describe('useStyles', () => {
+ it('sets z-index as css variables', () => {
+ vi.spyOn(global.document.documentElement.style, 'setProperty');
+
+ const { setAppZIndexes } = useStyles();
+
+ setAppZIndexes();
+
+ expect(global.document.documentElement.style.setProperty).toHaveBeenNthCalledWith(
+ 1,
+ '--z-index-app-header',
+ '99',
+ );
+ expect(global.document.documentElement.style.setProperty).toHaveBeenCalledWith(
+ '--z-index-canvas-add-button',
+ '101',
+ );
+ expect(global.document.documentElement.style.setProperty).toHaveBeenLastCalledWith(
+ '--z-index-workflow-preview-ndv',
+ '9999999',
+ );
+ });
+});
diff --git a/packages/editor-ui/src/composables/useStyles.ts b/packages/editor-ui/src/composables/useStyles.ts
new file mode 100644
index 0000000000..dc1db3ade3
--- /dev/null
+++ b/packages/editor-ui/src/composables/useStyles.ts
@@ -0,0 +1,31 @@
+const APP_Z_INDEXES = {
+ APP_HEADER: 99,
+ SELECT_BOX: 100,
+ CANVAS_ADD_BUTTON: 101,
+ NODE_CREATOR: 200,
+ ASK_ASSISTANT_CHAT: 300,
+ APP_SIDEBAR: 999,
+ CANVAS_SELECT_BOX: 100,
+ TOP_BANNERS: 999,
+ NDV: 1800,
+ MODALS: 2000,
+ TOASTS: 2100,
+ ASK_ASSISTANT_FLOATING_BUTTON: 3000,
+ ASK_ASSISTANT_FLOATING_BUTTON_TOOLTIP: 3000,
+ DRAGGABLE: 9999999,
+ ACTIVE_STICKY: 9999999,
+ WORKFLOW_PREVIEW_NDV: 9999999,
+} as const;
+
+const setAppZIndexes = () => {
+ Object.keys(APP_Z_INDEXES).forEach((key) => {
+ const variableName = `--z-index-${key.toLowerCase().replaceAll('_', '-')}`;
+ const value = APP_Z_INDEXES[key as keyof typeof APP_Z_INDEXES];
+ document.documentElement.style.setProperty(variableName, `${value}`);
+ });
+};
+
+export const useStyles = () => ({
+ APP_Z_INDEXES,
+ setAppZIndexes,
+});
diff --git a/packages/editor-ui/src/composables/useToast.ts b/packages/editor-ui/src/composables/useToast.ts
index 1ee8f4a2b4..7d07cdf249 100644
--- a/packages/editor-ui/src/composables/useToast.ts
+++ b/packages/editor-ui/src/composables/useToast.ts
@@ -9,6 +9,7 @@ import { useI18n } from './useI18n';
import { useExternalHooks } from './useExternalHooks';
import { VIEWS } from '@/constants';
import type { ApplicationError } from 'n8n-workflow';
+import { useStyles } from './useStyles';
export interface NotificationErrorWithNodeAndDescription extends ApplicationError {
node: {
@@ -17,15 +18,6 @@ export interface NotificationErrorWithNodeAndDescription extends ApplicationErro
description: string;
}
-const messageDefaults: Partial> = {
- dangerouslyUseHTMLString: false,
- position: 'bottom-right',
- zIndex: 1900, // above NDV and below the modals
- offset: 64,
- appendTo: '#app-grid',
- customClass: 'content-toast',
-};
-
const stickyNotificationQueue: NotificationHandle[] = [];
export function useToast() {
@@ -34,6 +26,16 @@ export function useToast() {
const uiStore = useUIStore();
const externalHooks = useExternalHooks();
const i18n = useI18n();
+ const { APP_Z_INDEXES } = useStyles();
+
+ const messageDefaults: Partial> = {
+ dangerouslyUseHTMLString: false,
+ position: 'bottom-right',
+ zIndex: APP_Z_INDEXES.TOASTS, // above NDV and modal overlays
+ offset: 64,
+ appendTo: '#app-grid',
+ customClass: 'content-toast',
+ };
function showMessage(messageData: Partial, track = true) {
const { message, title } = messageData;
diff --git a/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts b/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
index 760176321e..a4685e9e8d 100644
--- a/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
+++ b/packages/editor-ui/src/stores/__tests__/assistant.store.test.ts
@@ -18,6 +18,7 @@ import { reactive } from 'vue';
import * as chatAPI from '@/api/ai';
import * as telemetryModule from '@/composables/useTelemetry';
import type { Telemetry } from '@/plugins/telemetry';
+import type { ChatUI } from 'n8n-design-system/types/assistant';
let settingsStore: ReturnType;
let posthogStore: ReturnType;
@@ -417,4 +418,74 @@ describe('AI Assistant store', () => {
workflow_id: '__EMPTY__',
});
});
+
+ it('should call the function again if retry is called after handleServiceError', async () => {
+ const mockFn = vi.fn();
+
+ const assistantStore = useAssistantStore();
+
+ assistantStore.handleServiceError(new Error('test error'), '125', mockFn);
+ expect(assistantStore.chatMessages.length).toBe(1);
+ const message = assistantStore.chatMessages[0];
+ expect(message.type).toBe('error');
+
+ const errorMessage = message as ChatUI.ErrorMessage;
+ expect(errorMessage.retry).toBeDefined();
+
+ // This simulates the button click from the UI
+ await errorMessage.retry?.();
+
+ expect(mockFn).toHaveBeenCalled();
+ });
+
+ it('should properly clear messages on retry in a chat session', async () => {
+ const assistantStore = useAssistantStore();
+ const mockSessionId = 'mockSessionId';
+
+ const message: ChatRequest.MessageResponse = {
+ type: 'message',
+ role: 'assistant',
+ text: 'Hello!',
+ quickReplies: [
+ { text: 'Yes', type: 'text' },
+ { text: 'No', type: 'text' },
+ ],
+ };
+
+ apiSpy.mockImplementationOnce((_ctx, _payload, onMessage, onDone) => {
+ onMessage({ messages: [message], sessionId: mockSessionId });
+ onDone();
+ });
+ apiSpy.mockImplementationOnce((_ctx, _payload, _onMessage, _onDone, onError) =>
+ onError(new Error('test error')),
+ );
+ apiSpy.mockImplementationOnce((_ctx, _payload, onMessage, onDone) => {
+ onMessage({ messages: [message], sessionId: mockSessionId });
+ onDone();
+ });
+
+ await assistantStore.initSupportChat('hello');
+
+ expect(assistantStore.chatMessages.length).toBe(2);
+ expect(assistantStore.chatMessages[0].type).toBe('text');
+ expect(assistantStore.chatMessages[1].type).toBe('text');
+
+ await assistantStore.sendMessage({ text: 'test' });
+
+ expect(assistantStore.chatMessages.length).toBe(4);
+ expect(assistantStore.chatMessages[0].type).toBe('text');
+ expect(assistantStore.chatMessages[1].type).toBe('text');
+ expect(assistantStore.chatMessages[2].type).toBe('text');
+ expect(assistantStore.chatMessages[3].type).toBe('error');
+
+ expect(assistantStore.chatMessages[3]).toHaveProperty('retry');
+ // This simulates the functionality triggered from the consumer (e.g. UI Button)
+ await (assistantStore.chatMessages[3] as ChatUI.ErrorMessage).retry?.();
+
+ expect(assistantStore.chatMessages.length).toBe(4);
+ expect(assistantStore.chatMessages[0].type).toBe('text');
+ expect(assistantStore.chatMessages[1].type).toBe('text');
+ expect(assistantStore.chatMessages[2].type).toBe('text');
+ expect(assistantStore.chatMessages[3].type).toBe('text');
+ });
});
diff --git a/packages/editor-ui/src/stores/assistant.store.ts b/packages/editor-ui/src/stores/assistant.store.ts
index c3f8c77e00..02178c0784 100644
--- a/packages/editor-ui/src/stores/assistant.store.ts
+++ b/packages/editor-ui/src/stores/assistant.store.ts
@@ -251,13 +251,14 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
streaming.value = false;
}
- function addAssistantError(content: string, id: string) {
+ function addAssistantError(content: string, id: string, retry?: () => Promise) {
chatMessages.value.push({
id,
role: 'assistant',
type: 'error',
content,
read: true,
+ retry,
});
}
@@ -275,11 +276,15 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
});
}
- function handleServiceError(e: unknown, id: string) {
+ function handleServiceError(e: unknown, id: string, retry?: () => Promise) {
assert(e instanceof Error);
stopStreaming();
assistantThinkingMessage.value = undefined;
- addAssistantError(`${locale.baseText('aiAssistant.serviceError.message')}: (${e.message})`, id);
+ addAssistantError(
+ `${locale.baseText('aiAssistant.serviceError.message')}: (${e.message})`,
+ id,
+ retry,
+ );
}
function onEachStreamingMessage(response: ChatRequest.ResponsePayload, id: string) {
@@ -447,7 +452,8 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
},
(msg) => onEachStreamingMessage(msg, id),
() => onDoneStreaming(id),
- (e) => handleServiceError(e, id),
+ (e) =>
+ handleServiceError(e, id, async () => await initSupportChat(userMessage, credentialType)),
);
}
@@ -498,7 +504,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
},
(msg) => onEachStreamingMessage(msg, id),
() => onDoneStreaming(id),
- (e) => handleServiceError(e, id),
+ (e) => handleServiceError(e, id, async () => await initErrorHelper(context)),
);
}
@@ -527,7 +533,7 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
},
(msg) => onEachStreamingMessage(msg, id),
() => onDoneStreaming(id),
- (e) => handleServiceError(e, id),
+ (e) => handleServiceError(e, id, async () => await sendEvent(eventName, error)),
);
}
async function onNodeExecution(pushEvent: PushPayload<'nodeExecuteAfter'>) {
@@ -564,6 +570,12 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
}
const id = getRandomId();
+
+ const retry = async () => {
+ chatMessages.value = chatMessages.value.filter((msg) => msg.id !== id);
+ await sendMessage(chatMessage);
+ };
+
try {
addUserMessage(chatMessage.text, id);
addLoadingAssistantMessage(locale.baseText('aiAssistant.thinkingSteps.thinking'));
@@ -594,12 +606,12 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
},
(msg) => onEachStreamingMessage(msg, id),
() => onDoneStreaming(id),
- (e) => handleServiceError(e, id),
+ (e) => handleServiceError(e, id, retry),
);
trackUserMessage(chatMessage.text, !!chatMessage.quickReplyType);
} catch (e: unknown) {
// in case of assert
- handleServiceError(e, id);
+ handleServiceError(e, id, retry);
}
}
@@ -824,5 +836,6 @@ export const useAssistantStore = defineStore(STORES.ASSISTANT, () => {
chatSessionTask,
initCredHelp,
isCredTypeActive,
+ handleServiceError,
};
});
diff --git a/packages/editor-ui/src/views/CanvasAddButton.vue b/packages/editor-ui/src/views/CanvasAddButton.vue
index ff7b37b4e3..108cf321e8 100644
--- a/packages/editor-ui/src/views/CanvasAddButton.vue
+++ b/packages/editor-ui/src/views/CanvasAddButton.vue
@@ -55,7 +55,7 @@ const containerCssVars = computed(() => ({
left: var(--trigger-placeholder-left-position);
// We have to increase z-index to make sure it's higher than selecting box in NodeView
// otherwise the clicks wouldn't register
- z-index: 101;
+ z-index: var(--z-index-canvas-add-button);
&:hover .button svg path {
fill: var(--color-primary);