diff --git a/src/interface/desktop/assets/icons/trash-solid.svg b/src/interface/desktop/assets/icons/trash-solid.svg new file mode 100644 index 00000000..768d80f8 --- /dev/null +++ b/src/interface/desktop/assets/icons/trash-solid.svg @@ -0,0 +1 @@ + diff --git a/src/interface/desktop/chat.html b/src/interface/desktop/chat.html index ecd8ebf9..a436b593 100644 --- a/src/interface/desktop/chat.html +++ b/src/interface/desktop/chat.html @@ -515,6 +515,32 @@ chat(); } } + + async function clearConversationHistory() { + let chatInput = document.getElementById("chat-input"); + let originalPlaceholder = chatInput.placeholder; + let chatBody = document.getElementById("chat-body"); + + const hostURL = await window.hostURLAPI.getURL(); + const khojToken = await window.tokenAPI.getToken(); + const headers = { 'Authorization': `Bearer ${khojToken}` }; + + fetch(`${hostURL}/api/chat/history?client=desktop`, { method: "DELETE", headers }) + .then(response => response.ok ? response.json() : Promise.reject(response)) + .then(data => { + chatBody.innerHTML = ""; + loadChat(); + chatInput.placeholder = "Cleared conversation history"; + }) + .catch(err => { + chatInput.placeholder = "Failed to clear conversation history"; + }) + .finally(() => { + setTimeout(() => { + chatInput.placeholder = originalPlaceholder; + }, 2000); + }); + }
@@ -541,7 +567,12 @@ @@ -655,15 +686,17 @@ #chat-footer { padding: 0; + margin: 8px; display: grid; grid-template-columns: minmax(70px, 100%); grid-column-gap: 10px; grid-row-gap: 10px; } - #chat-footer > * { - padding: 15px; - border-radius: 5px; - border: 1px solid #475569; + #input-row { + display: grid; + grid-template-columns: auto 32px; + grid-column-gap: 10px; + grid-row-gap: 10px; background: #f9fafc } .option:hover { @@ -684,6 +717,26 @@ #chat-input:focus { outline: none !important; } + .input-row-button { + background: var(--background-color); + border: none; + border-radius: 5px; + padding: 5px; + font-size: 14px; + font-weight: 300; + line-height: 1.5em; + cursor: pointer; + transition: background 0.3s ease-in-out; + } + .input-row-button:hover { + background: var(--primary-hover); + } + .input-row-button:active { + background: var(--primary-active); + } + .input-row-button-img { + width: 24px; + } .option-enabled { box-shadow: 0 0 12px rgb(119, 156, 46); diff --git a/src/interface/obsidian/src/chat_modal.ts b/src/interface/obsidian/src/chat_modal.ts index a8008048..fc6d5a48 100644 --- a/src/interface/obsidian/src/chat_modal.ts +++ b/src/interface/obsidian/src/chat_modal.ts @@ -1,4 +1,4 @@ -import { App, Modal, request } from 'obsidian'; +import { App, Modal, request, setIcon } from 'obsidian'; import { KhojSetting } from 'src/settings'; import fetch from "node-fetch"; @@ -38,7 +38,8 @@ export class KhojChatModal extends Modal { await this.getChatHistory(); // Add chat input field - const chatInput = contentEl.createEl("input", + let inputRow = contentEl.createDiv("khoj-input-row"); + const chatInput = inputRow.createEl("input", { attr: { type: "text", @@ -50,6 +51,15 @@ export class KhojChatModal extends Modal { }) chatInput.addEventListener('change', (event) => { this.result = (event.target).value }); + let clearChat = inputRow.createEl("button", { + text: "Clear History", + attr: { + class: "khoj-input-row-button", + }, + }) + clearChat.addEventListener('click', async (_) => { await this.clearConversationHistory() }); + setIcon(clearChat, "trash"); + // Scroll to bottom of modal, till the send message input box this.modalEl.scrollTop = this.modalEl.scrollHeight; chatInput.focus(); @@ -194,4 +204,35 @@ export class KhojChatModal extends Modal { this.renderIncrementalMessage(responseElement, "Sorry, unable to get response from Khoj backend ❤️‍🩹. Contact developer for help at team@khoj.dev or in Discord") } } + + async clearConversationHistory() { + let chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0]; + let originalPlaceholder = chatInput.placeholder; + let chatBody = this.contentEl.getElementsByClassName("khoj-chat-body")[0]; + + let response = await request({ + url: `${this.setting.khojUrl}/api/chat/history?client=web`, + method: "DELETE", + headers: { "Authorization": `Bearer ${this.setting.khojApiKey}` }, + }) + try { + let result = JSON.parse(response); + if (result.status !== "ok") { + // Throw error if conversation history isn't cleared + throw new Error("Failed to clear conversation history"); + } else { + // If conversation history is cleared successfully, clear chat logs from modal + chatBody.innerHTML = ""; + await this.getChatHistory(); + chatInput.placeholder = result.message; + } + } catch (err) { + chatInput.placeholder = "Failed to clear conversation history"; + } finally { + // Reset to original placeholder text after some time + setTimeout(() => { + chatInput.placeholder = originalPlaceholder; + }, 2000); + } + } } diff --git a/src/interface/obsidian/styles.css b/src/interface/obsidian/styles.css index d322804d..95a304f1 100644 --- a/src/interface/obsidian/styles.css +++ b/src/interface/obsidian/styles.css @@ -68,7 +68,7 @@ If your plugin does not need CSS, delete this file. } /* color chat bubble by khoj blue */ .khoj-chat-message-text.khoj { - color: var(--text-on-accent); + color: var(--khoj-chat-dark-grey); background: var(--khoj-chat-primary); margin-left: auto; white-space: pre-line; @@ -110,9 +110,12 @@ If your plugin does not need CSS, delete this file. grid-column-gap: 10px; grid-row-gap: 10px; } -#khoj-chat-footer > * { - padding: 15px; - background: #f9fafc +.khoj-input-row { + display: grid; + grid-template-columns: auto 32px; + grid-column-gap: 10px; + grid-row-gap: 10px; + background: var(--background-primary); } #khoj-chat-input.option:hover { box-shadow: 0 0 11px var(--background-modifier-box-shadow); @@ -121,6 +124,25 @@ If your plugin does not need CSS, delete this file. font-size: var(--font-ui-medium); padding: 25px 20px; } +.khoj-input-row-button { + background: var(--background-primary); + border: none; + border-radius: 5px; + padding: 5px; + --icon-size: var(--icon-size); + height: auto; + font-size: 14px; + font-weight: 300; + line-height: 1.5em; + cursor: pointer; + transition: background 0.3s ease-in-out; +} +.khoj-input-row-button:hover { + background: var(--background-modifier-hover); +} +.khoj-input-row-button:active { + background: var(--background-modifier-active); +} @media (pointer: coarse), (hover: none) { #khoj-chat-body.abbr[title] { diff --git a/src/khoj/database/adapters/__init__.py b/src/khoj/database/adapters/__init__.py index 7fd04006..de5f1b5d 100644 --- a/src/khoj/database/adapters/__init__.py +++ b/src/khoj/database/adapters/__init__.py @@ -233,6 +233,10 @@ class ConversationAdapters: return await conversation.afirst() return await Conversation.objects.acreate(user=user) + @staticmethod + async def adelete_conversation_by_user(user: KhojUser): + return await Conversation.objects.filter(user=user).adelete() + @staticmethod def has_any_conversation_config(user: KhojUser): return ChatModelOptions.objects.filter(user=user).exists() diff --git a/src/khoj/interface/web/chat.html b/src/khoj/interface/web/chat.html index abab83ab..8d2accc4 100644 --- a/src/khoj/interface/web/chat.html +++ b/src/khoj/interface/web/chat.html @@ -468,7 +468,9 @@ To get started, just start typing below. You can also type / to see a list of co document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight; } - window.onload = function () { + window.onload = loadChat; + + function loadChat() { fetch('/api/chat/history?client=web') .then(response => response.json()) .then(data => { @@ -540,6 +542,28 @@ To get started, just start typing below. You can also type / to see a list of co chat(); } } + + function clearConversationHistory() { + let chatInput = document.getElementById("chat-input"); + let originalPlaceholder = chatInput.placeholder; + let chatBody = document.getElementById("chat-body"); + + fetch(`/api/chat/history?client=web`, { method: "DELETE" }) + .then(response => response.ok ? response.json() : Promise.reject(response)) + .then(data => { + chatBody.innerHTML = ""; + loadChat(); + chatInput.placeholder = "Cleared conversation history"; + }) + .catch(err => { + chatInput.placeholder = "Failed to clear conversation history"; + }) + .finally(() => { + setTimeout(() => { + chatInput.placeholder = originalPlaceholder; + }, 2000); + }); + }
@@ -558,7 +582,12 @@ To get started, just start typing below. You can also type / to see a list of co