From f46b95300b3aa859046d6cf29e0f00fd161e1b6c Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Fri, 18 Oct 2024 23:54:35 -0700 Subject: [PATCH] feat: rich text input for chat --- cypress/e2e/chat.cy.ts | 6 +- src/lib/components/chat/Chat.svelte | 15 +- src/lib/components/chat/MessageInput.svelte | 352 ++++++++++-------- .../chat/MessageInput/Commands.svelte | 6 +- .../MessageInput/Commands/Knowledge.svelte | 6 +- .../chat/MessageInput/Commands/Models.svelte | 2 +- .../chat/MessageInput/Commands/Prompts.svelte | 2 +- .../chat/MessageInput/VoiceRecording.svelte | 2 +- src/lib/components/chat/Messages.svelte | 2 +- src/lib/components/chat/Placeholder.svelte | 2 +- .../components/common/RichTextInput.svelte | 42 ++- src/lib/utils/index.ts | 3 +- src/routes/(app)/+layout.svelte | 2 +- static/themes/rosepine-dawn.css | 2 +- static/themes/rosepine.css | 2 +- 15 files changed, 268 insertions(+), 178 deletions(-) diff --git a/cypress/e2e/chat.cy.ts b/cypress/e2e/chat.cy.ts index 20be9755a..17c4d8e73 100644 --- a/cypress/e2e/chat.cy.ts +++ b/cypress/e2e/chat.cy.ts @@ -30,7 +30,7 @@ describe('Settings', () => { // Select the first model cy.get('button[aria-label="model-item"]').first().click(); // Type a message - cy.get('#chat-textarea').type('Hi, what can you do? A single sentence only please.', { + cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', { force: true }); // Send the message @@ -50,7 +50,7 @@ describe('Settings', () => { // Select the first model cy.get('button[aria-label="model-item"]').first().click(); // Type a message - cy.get('#chat-textarea').type('Hi, what can you do? A single sentence only please.', { + cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', { force: true }); // Send the message @@ -85,7 +85,7 @@ describe('Settings', () => { // Select the first model cy.get('button[aria-label="model-item"]').first().click(); // Type a message - cy.get('#chat-textarea').type('Hi, what can you do? A single sentence only please.', { + cy.get('#chat-input').type('Hi, what can you do? A single sentence only please.', { force: true }); // Send the message diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index c0e053899..bb6bdd270 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -125,7 +125,7 @@ loaded = true; window.setTimeout(() => scrollToBottom(), 0); - const chatInput = document.getElementById('chat-textarea'); + const chatInput = document.getElementById('chat-input'); chatInput?.focus(); } else { await goto('/'); @@ -264,7 +264,7 @@ if (event.data.type === 'input:prompt') { console.debug(event.data.text); - const inputElement = document.getElementById('chat-textarea'); + const inputElement = document.getElementById('chat-input'); if (inputElement) { prompt = event.data.text; @@ -327,7 +327,7 @@ } }); - const chatInput = document.getElementById('chat-textarea'); + const chatInput = document.getElementById('chat-input'); chatInput?.focus(); chats.subscribe(() => {}); @@ -501,7 +501,7 @@ settings.set(JSON.parse(localStorage.getItem('settings') ?? '{}')); } - const chatInput = document.getElementById('chat-textarea'); + const chatInput = document.getElementById('chat-input'); setTimeout(() => chatInput?.focus(), 0); }; @@ -799,7 +799,7 @@ ); } else { // Reset chat input textarea - const chatTextAreaElement = document.getElementById('chat-textarea'); + const chatTextAreaElement = document.getElementById('chat-input'); if (chatTextAreaElement) { chatTextAreaElement.value = ''; @@ -841,6 +841,11 @@ // Wait until history/message have been updated await tick(); + + // focus on chat input + const chatInput = document.getElementById('chat-input'); + chatInput?.focus(); + _responses = await sendPrompt(userPrompt, userMessageId, { newChat: true }); } diff --git a/src/lib/components/chat/MessageInput.svelte b/src/lib/components/chat/MessageInput.svelte index 50b8f1a72..677c96979 100644 --- a/src/lib/components/chat/MessageInput.svelte +++ b/src/lib/components/chat/MessageInput.svelte @@ -29,6 +29,7 @@ import FilesOverlay from './MessageInput/FilesOverlay.svelte'; import Commands from './MessageInput/Commands.svelte'; import XMark from '../icons/XMark.svelte'; + import RichTextInput from '../common/RichTextInput.svelte'; const i18n = getContext('i18n'); @@ -53,8 +54,8 @@ let recording = false; let chatTextAreaElement: HTMLTextAreaElement; + let chatInputContainerElement; let filesInputElement; - let commandsElement; let inputFiles; @@ -213,7 +214,10 @@ }; onMount(() => { - window.setTimeout(() => chatTextAreaElement?.focus(), 0); + window.setTimeout(() => { + const chatInput = document.getElementById('chat-input'); + chatInput?.focus(); + }, 0); window.addEventListener('keydown', handleKeyDown); @@ -351,7 +355,7 @@ recording = false; await tick(); - document.getElementById('chat-textarea')?.focus(); + document.getElementById('chat-input')?.focus(); }} on:confirm={async (e) => { const response = e.detail; @@ -360,7 +364,7 @@ recording = false; await tick(); - document.getElementById('chat-textarea')?.focus(); + document.getElementById('chat-input')?.focus(); if ($settings?.speechAutoSend ?? false) { dispatch('submit', prompt); @@ -500,8 +504,195 @@ -