diff --git a/packages/frontend/editor-ui/src/components/CanvasChat/composables/useChatState.ts b/packages/frontend/editor-ui/src/components/CanvasChat/composables/useChatState.ts index 03fbfae9ae..8cfda2ac54 100644 --- a/packages/frontend/editor-ui/src/components/CanvasChat/composables/useChatState.ts +++ b/packages/frontend/editor-ui/src/components/CanvasChat/composables/useChatState.ts @@ -207,6 +207,10 @@ export function useChatState(isReadOnly: boolean, onWindowResize?: () => void): nodeHelpers.updateNodesExecutionIssues(); messages.value = []; currentSessionId.value = uuid().replace(/-/g, ''); + + if (logsPanelState.value !== LOGS_PANEL_STATE.CLOSED) { + chatEventBus.emit('focusInput'); + } } function displayExecution(executionId: string) { diff --git a/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.test.ts b/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.test.ts index 7aad952e23..86de59fc28 100644 --- a/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.test.ts +++ b/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.test.ts @@ -141,15 +141,17 @@ describe('LogsPanel', () => { await fireEvent.click(await rendered.findByTestId('logs-overview-header')); await fireEvent.click(await rendered.findByText('AI Agent')); - const detailsPanel = rendered.getByTestId('log-details'); - // Click the toggle button to close the panel - await fireEvent.click(within(detailsPanel).getByLabelText('Collapse panel')); + await fireEvent.click( + within(rendered.getByTestId('log-details')).getByLabelText('Collapse panel'), + ); expect(rendered.queryByTestId('chat-messages-empty')).not.toBeInTheDocument(); expect(rendered.queryByTestId('logs-overview-body')).not.toBeInTheDocument(); // Click again to open the panel - await fireEvent.click(within(detailsPanel).getByLabelText('Open panel')); + await fireEvent.click( + within(rendered.getByTestId('logs-overview')).getByLabelText('Open panel'), + ); expect(await rendered.findByTestId('chat-messages-empty')).toBeInTheDocument(); expect(await rendered.findByTestId('logs-overview-body')).toBeInTheDocument(); }); diff --git a/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.vue b/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.vue index d78f89c4df..3be60ae895 100644 --- a/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.vue +++ b/packages/frontend/editor-ui/src/components/CanvasChat/future/LogsPanel.vue @@ -77,10 +77,10 @@ const selectedLogEntry = computed(() => ? undefined : manualLogEntrySelection.value.data, ); -const isLogDetailsOpen = computed( - () => selectedLogEntry.value !== undefined && !isCollapsingDetailsPanel.value, +const isLogDetailsOpen = computed(() => isOpen.value && selectedLogEntry.value !== undefined); +const isLogDetailsVisuallyOpen = computed( + () => isLogDetailsOpen.value && !isCollapsingDetailsPanel.value, ); -const isLogDetailsOpenOrCollapsing = computed(() => selectedLogEntry.value !== undefined); const logsPanelActionsProps = computed['$props']>(() => ({ isOpen: isOpen.value, showToggleButton: !isPoppedOut.value, @@ -149,9 +149,9 @@ function handleResizeOverviewPanelEnd() { diff --git a/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.test.ts b/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.test.ts index ad486c6468..959f2f85ef 100644 --- a/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.test.ts +++ b/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.test.ts @@ -92,9 +92,13 @@ describe('LogDetailsPanel', () => { createdAt: '2025-04-16T00:00:00.000Z', startedAt: '2025-04-16T00:00:01.000Z', }); + + localStorage.clear(); }); it('should show name, run status, input, and output of the node', async () => { + localStorage.setItem('N8N_LOGS_DETAIL_PANEL_CONTENT', 'both'); + const rendered = render({ isOpen: true, logEntry: createTestLogEntry({ node: 'AI Agent', runIndex: 0 }), @@ -118,12 +122,12 @@ describe('LogDetailsPanel', () => { const header = within(rendered.getByTestId('log-details-header')); - expect(rendered.queryByTestId('log-details-input')).toBeInTheDocument(); + expect(rendered.queryByTestId('log-details-input')).not.toBeInTheDocument(); expect(rendered.queryByTestId('log-details-output')).toBeInTheDocument(); await fireEvent.click(header.getByText('Input')); - expect(rendered.queryByTestId('log-details-input')).not.toBeInTheDocument(); + expect(rendered.queryByTestId('log-details-input')).toBeInTheDocument(); expect(rendered.queryByTestId('log-details-output')).toBeInTheDocument(); await fireEvent.click(header.getByText('Output')); @@ -133,6 +137,8 @@ describe('LogDetailsPanel', () => { }); it('should close input panel by dragging the divider to the left end', async () => { + localStorage.setItem('N8N_LOGS_DETAIL_PANEL_CONTENT', 'both'); + const rendered = render({ isOpen: true, logEntry: createTestLogEntry({ node: 'AI Agent', runIndex: 0 }), @@ -150,6 +156,8 @@ describe('LogDetailsPanel', () => { }); it('should close output panel by dragging the divider to the right end', async () => { + localStorage.setItem('N8N_LOGS_DETAIL_PANEL_CONTENT', 'both'); + const rendered = render({ isOpen: true, logEntry: createTestLogEntry({ node: 'AI Agent', runIndex: 0 }), diff --git a/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.vue b/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.vue index 74c3860122..63da30b74d 100644 --- a/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.vue +++ b/packages/frontend/editor-ui/src/components/CanvasChat/future/components/LogDetailsPanel.vue @@ -12,8 +12,9 @@ import { type INodeUi } from '@/Interface'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { useWorkflowsStore } from '@/stores/workflows.store'; import { N8nButton, N8nResizeWrapper, N8nText } from '@n8n/design-system'; +import { useLocalStorage } from '@vueuse/core'; import { type ITaskData } from 'n8n-workflow'; -import { computed, ref, useTemplateRef } from 'vue'; +import { computed, useTemplateRef } from 'vue'; const MIN_IO_PANEL_WIDTH = 200; @@ -32,7 +33,11 @@ const telemetry = useTelemetry(); const workflowsStore = useWorkflowsStore(); const nodeTypeStore = useNodeTypesStore(); -const content = ref(LOG_DETAILS_CONTENT.BOTH); +const content = useLocalStorage( + 'N8N_LOGS_DETAIL_PANEL_CONTENT', + LOG_DETAILS_CONTENT.OUTPUT, + { writeDefaults: false }, +); const node = computed(() => workflowsStore.nodesByName[logEntry.node]); const type = computed(() => (node.value ? nodeTypeStore.getNodeType(node.value.type) : undefined)); @@ -100,7 +105,11 @@ function handleResizeEnd() {