From 246f8cfcc3acdeb323849c94542fc4ad028c4f77 Mon Sep 17 00:00:00 2001 From: oleg Date: Thu, 22 Feb 2024 09:32:34 +0100 Subject: [PATCH] feat(editor): Retrieve previous chat message on arrow-up (#8696) Signed-off-by: Oleg Ivaniv --- packages/editor-ui/src/Interface.ts | 1 + .../src/components/WorkflowLMChat.vue | 27 ++++++++++++++++--- .../src/plugins/i18n/locales/en.json | 2 +- .../editor-ui/src/stores/workflows.store.ts | 12 +++++++++ packages/editor-ui/src/views/NodeView.vue | 1 + 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 20eadf1089..f80a448569 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1073,6 +1073,7 @@ export interface WorkflowsState { workflowExecutionData: IExecutionResponse | null; workflowExecutionPairedItemMappings: { [itemId: string]: Set }; workflowsById: IWorkflowsMap; + chatMessages: string[]; isInDebugMode?: boolean; } diff --git a/packages/editor-ui/src/components/WorkflowLMChat.vue b/packages/editor-ui/src/components/WorkflowLMChat.vue index 1c2b78e4d2..054e9e7f66 100644 --- a/packages/editor-ui/src/components/WorkflowLMChat.vue +++ b/packages/editor-ui/src/components/WorkflowLMChat.vue @@ -66,7 +66,7 @@ -
+
{{ $locale.baseText('chat.window.logs') }} @@ -149,8 +149,8 @@ import { useExternalHooks } from '@/composables/useExternalHooks'; // eslint-disable-next-line import/no-unresolved import MessageTyping from '@n8n/chat/components/MessageTyping.vue'; -import { useRouter } from 'vue-router'; import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; +import { useRouter } from 'vue-router'; import { useNodeTypesStore } from '@/stores/nodeTypes.store'; const RunDataAi = defineAsyncComponent( @@ -183,8 +183,8 @@ export default defineComponent({ }, mixins: [workflowRun], setup(props, ctx) { - const externalHooks = useExternalHooks(); const router = useRouter(); + const externalHooks = useExternalHooks(); const workflowHelpers = useWorkflowHelpers(router); return { @@ -203,6 +203,7 @@ export default defineComponent({ modalBus: createEventBus(), node: null as INodeUi | null, WORKFLOW_LM_CHAT_MODAL_KEY, + previousMessageIndex: 0, }; }, @@ -216,6 +217,8 @@ export default defineComponent({ this.setConnectedNode(); this.messages = this.getChatMessages(); this.setNode(); + + setTimeout(() => this.$refs.inputField?.focus(), 0); }, methods: { displayExecution(executionId: string) { @@ -235,6 +238,20 @@ export default defineComponent({ inputField.focus(); }, updated(event: KeyboardEvent) { + const pastMessages = this.workflowsStore.getPastChatMessages; + if ( + (this.currentMessage.length === 0 || pastMessages.includes(this.currentMessage)) && + event.key === 'ArrowUp' + ) { + const inputField = this.$refs.inputField as HTMLInputElement; + + inputField?.blur(); + this.currentMessage = + pastMessages[pastMessages.length - 1 - this.previousMessageIndex] ?? ''; + this.previousMessageIndex = (this.previousMessageIndex + 1) % pastMessages.length; + // Refocus to move the cursor to the end of the input + setTimeout(() => inputField?.focus(), 0); + } if (event.key === 'Enter' && !event.shiftKey && this.currentMessage) { void this.sendChatMessage(this.currentMessage); event.stopPropagation(); @@ -255,6 +272,7 @@ export default defineComponent({ } as ChatMessage); this.currentMessage = ''; + this.previousMessageIndex = 0; await this.$nextTick(); this.scrollToLatestMessage(); await this.startWorkflowWithMessage(message); @@ -272,7 +290,7 @@ export default defineComponent({ } const workflow = this.workflowHelpers.getCurrentWorkflow(); - const chatNode = this.workflowHelpers.getNodes().find((node: INodeUi): boolean => { + const chatNode = this.workflowsStore.getNodes().find((node: INodeUi): boolean => { const nodeType = this.nodeTypesStore.getNodeType(node.type, node.typeVersion); if (!nodeType) return false; @@ -454,6 +472,7 @@ export default defineComponent({ source: 'RunData.ManualChatMessage', }); + this.workflowsStore.appendChatMessage(message); if (!response) { this.showError( new Error('It was not possible to start workflow!'), diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 5d2d4b003f..934928236f 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -140,7 +140,7 @@ "chat.window.logs": "Log (for last message)", "chat.window.noChatNode": "No Chat Node", "chat.window.noExecution": "Nothing got executed yet", - "chat.window.chat.placeholder": "Type in message", + "chat.window.chat.placeholder": "Type a message, or press ‘up’ arrow for previous one", "chat.window.chat.sendButtonText": "Send", "chat.window.chat.provideMessage": "Please provide a message", "chat.window.chat.emptyChatMessage": "Empty chat message", diff --git a/packages/editor-ui/src/stores/workflows.store.ts b/packages/editor-ui/src/stores/workflows.store.ts index 4a8a74ce28..9489b9f95b 100644 --- a/packages/editor-ui/src/stores/workflows.store.ts +++ b/packages/editor-ui/src/stores/workflows.store.ts @@ -125,6 +125,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, { executionWaitingForWebhook: false, nodeMetadata: {}, isInDebugMode: false, + chatMessages: [], }), getters: { // Workflow getters @@ -277,6 +278,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, { getTotalFinishedExecutionsCount(): number { return this.finishedExecutionsCount; }, + getPastChatMessages(): string[] { + return Array.from(new Set(this.chatMessages)); + }, }, actions: { getPinDataSize(pinData: Record = {}): number { @@ -1436,5 +1440,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, { }, }; }, + + resetChatMessages(): void { + this.chatMessages = []; + }, + + appendChatMessage(message: string): void { + this.chatMessages.push(message); + }, }, }); diff --git a/packages/editor-ui/src/views/NodeView.vue b/packages/editor-ui/src/views/NodeView.vue index a1fa81c85b..07d7fda0a8 100644 --- a/packages/editor-ui/src/views/NodeView.vue +++ b/packages/editor-ui/src/views/NodeView.vue @@ -1013,6 +1013,7 @@ export default defineComponent({ this.instance.unbind(); this.instance.destroy(); this.uiStore.stateIsDirty = false; + this.workflowsStore.resetChatMessages(); window.removeEventListener('message', this.onPostMessageReceived); nodeViewEventBus.off('newWorkflow', this.newWorkflow); nodeViewEventBus.off('importWorkflowData', this.onImportWorkflowDataEvent);