diff --git a/src/interface/desktop/chat.html b/src/interface/desktop/chat.html
index 7cd75f01..35605b64 100644
--- a/src/interface/desktop/chat.html
+++ b/src/interface/desktop/chat.html
@@ -546,7 +546,7 @@
const textarea = document.getElementById('chat-input');
const scrollTop = textarea.scrollTop;
textarea.style.height = '0';
- const scrollHeight = textarea.scrollHeight;
+ const scrollHeight = textarea.scrollHeight + 8; // +8 accounts for padding
textarea.style.height = Math.min(scrollHeight, 200) + 'px';
textarea.scrollTop = scrollTop;
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
@@ -599,7 +599,7 @@
.then(response => response.json())
.then(data => {
// Render chat options, if any
- if (data) {
+ if (data.length > 0) {
let questionStarterSuggestions = document.getElementById("question-starters");
for (let index in data) {
let questionStarter = data[index];
@@ -673,9 +673,14 @@
})
}
+ let sendMessageTimeout;
let mediaRecorder;
- async function speechToText() {
+ async function speechToText(event) {
+ event.preventDefault();
const speakButtonImg = document.getElementById('speak-button-img');
+ const stopRecordButtonImg = document.getElementById('stop-record-button-img');
+ const sendButtonImg = document.getElementById('send-button-img');
+ const stopSendButtonImg = document.getElementById('stop-send-button-img');
const chatInput = document.getElementById('chat-input');
const hostURL = await window.hostURLAPI.getURL();
@@ -689,7 +694,30 @@
fetch(url, { method: 'POST', body: formData, headers})
.then(response => response.ok ? response.json() : Promise.reject(response))
- .then(data => { chatInput.value += data.text; })
+ .then(data => { chatInput.value += data.text.trimStart(); autoResize(); })
+ .then(() => {
+ // Don't auto-send empty messages
+ if (chatInput.value.length === 0) return;
+
+ // Send message after 3 seconds, unless stop send button is clicked
+ sendButtonImg.style.display = 'none';
+ stopSendButtonImg.style.display = 'initial';
+
+ // Start the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "countdown 3s linear 1 forwards";
+
+ sendMessageTimeout = setTimeout(() => {
+ // Revert to showing send-button and hide the stop-send-button
+ sendButtonImg.style.display = 'initial';
+ stopSendButtonImg.style.display = 'none';
+
+ // Stop the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "none";
+
+ // Send message
+ chat();
+ }, 3000);
+ })
.catch(err => {
if (err.status === 501) {
flashStatusInChatInput("⛔️ Configure speech-to-text model on server.")
@@ -716,27 +744,38 @@
});
mediaRecorder.start();
- speakButtonImg.src = './assets/icons/stop-solid.svg';
- speakButtonImg.alt = 'Stop Transcription';
+ speakButtonImg.style.display = 'none';
+ stopRecordButtonImg.style.display = 'initial';
};
// Toggle recording
- if (!mediaRecorder || mediaRecorder.state === 'inactive') {
+ if (!mediaRecorder || mediaRecorder.state === 'inactive' || event.type === 'touchstart') {
navigator.mediaDevices
- .getUserMedia({ audio: true })
+ ?.getUserMedia({ audio: true })
.then(handleRecording)
.catch((e) => {
flashStatusInChatInput("⛔️ Failed to access microphone");
});
- } else if (mediaRecorder.state === 'recording') {
+ } else if (mediaRecorder.state === 'recording' || event.type === 'touchend' || event.type === 'touchcancel') {
mediaRecorder.stop();
mediaRecorder.stream.getTracks().forEach(track => track.stop());
mediaRecorder = null;
- speakButtonImg.src = './assets/icons/microphone-solid.svg';
- speakButtonImg.alt = 'Transcribe';
+ speakButtonImg.style.display = 'initial';
+ stopRecordButtonImg.style.display = 'none';
}
}
+ function cancelSendMessage() {
+ // Cancel the chat() call if the stop-send-button is clicked
+ clearTimeout(sendMessageTimeout);
+
+ // Revert to showing send-button and hide the stop-send-button
+ document.getElementById('stop-send-button-img').style.display = 'none';
+ document.getElementById('send-button-img').style.display = 'initial';
+
+ // Stop the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "none";
+ };
@@ -764,12 +803,36 @@
@@ -894,10 +957,11 @@
}
#input-row {
display: grid;
- grid-template-columns: auto 32px 32px;
+ grid-template-columns: 32px auto 32px 40px;
grid-column-gap: 10px;
grid-row-gap: 10px;
- background: #f9fafc
+ background: #f9fafc;
+ align-items: center;
}
.option:hover {
box-shadow: 0 0 11px #aaa;
@@ -905,12 +969,13 @@
#chat-input {
font-family: roboto, karma, segoe ui, sans-serif;
font-size: small;
- height: 54px;
+ height: 36px;
+ border-radius: 16px;
resize: none;
overflow-y: hidden;
max-height: 200px;
box-sizing: border-box;
- padding: 15px;
+ padding: 7px 0 0 12px;
line-height: 1.5em;
margin: 0;
}
@@ -919,15 +984,19 @@
}
.input-row-button {
background: var(--background-color);
- border: 1px solid var(--main-text-color);
- box-shadow: 0 0 11px #aaa;
- border-radius: 5px;
+ border: none;
+ box-shadow: none;
+ border-radius: 50%;
font-size: 14px;
font-weight: 300;
padding: 0;
line-height: 1.5em;
cursor: pointer;
transition: background 0.3s ease-in-out;
+ width: 40px;
+ height: 40px;
+ margin-top: -2px;
+ margin-left: -5px;
}
.input-row-button:hover {
background: var(--primary-hover);
@@ -937,6 +1006,41 @@
}
.input-row-button-img {
width: 24px;
+ height: 24px;
+ }
+ #send-button {
+ padding: 0;
+ position: relative;
+ }
+ #send-button-img {
+ width: 28px;
+ height: 28px;
+ background: var(--primary-hover);
+ border-radius: 50%;
+ }
+ #stop-send-button-img {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 28px;
+ height: 28px;
+ transform: rotateY(-180deg) rotateZ(-90deg);
+ }
+ #countdown-circle {
+ stroke-dasharray: 44px; /* The circumference of the circle with 7px radius */
+ stroke-dashoffset: 0px;
+ stroke-linecap: round;
+ stroke-width: 1px;
+ stroke: var(--main-text-color);
+ fill: none;
+ }
+ @keyframes countdown {
+ from {
+ stroke-dashoffset: 0px;
+ }
+ to {
+ stroke-dashoffset: -44px; /* The circumference of the circle with 7px radius */
+ }
}
.option-enabled {
@@ -978,7 +1082,7 @@
background: var(--background-color);
color: var(--main-text-color);
border: 1px solid var(--main-text-color);
- border-radius: 5px;
+ border-radius: 16px;
padding: 5px;
font-size: 14px;
font-weight: 300;
@@ -990,6 +1094,9 @@
transition: max-height 0.3s ease-in-out;
overflow: hidden;
}
+ button.question-starter:hover {
+ background: var(--primary-hover);
+ }
code.chat-response {
background: var(--primary-hover);
@@ -1106,7 +1213,7 @@
color: #f8fafc;
border-radius: 2px;
box-shadow: 1px 1px 4px 0 rgba(0, 0, 0, 0.4);
- font-size small;
+ font-size: small;
padding: 2px 4px;
}
}
@@ -1126,6 +1233,9 @@
img.text-to-image {
max-width: 100%;
}
+ #clear-chat-button {
+ margin-left: 0;
+ }
}
@media only screen and (min-width: 600px) {
body {
diff --git a/src/interface/obsidian/src/chat_modal.ts b/src/interface/obsidian/src/chat_modal.ts
index 0f597e2a..1b35f499 100644
--- a/src/interface/obsidian/src/chat_modal.ts
+++ b/src/interface/obsidian/src/chat_modal.ts
@@ -17,17 +17,20 @@ export class KhojChatModal extends Modal {
this.setting = setting;
// Register Modal Keybindings to send user message
- this.scope.register([], 'Enter', async () => {
- // Get text in chat input elmenet
- let input_el =
this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ this.scope.register([], 'Enter', async () => { await this.chat() });
+ }
- // Clear text after extracting message to send
- let user_message = input_el.value;
- input_el.value = "";
+ async chat() {
+ // Get text in chat input element
+ let input_el = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
- // Get and render chat response to user message
- await this.getChatResponse(user_message);
- });
+ // Clear text after extracting message to send
+ let user_message = input_el.value.trim();
+ input_el.value = "";
+ this.autoResize();
+
+ // Get and render chat response to user message
+ await this.getChatResponse(user_message);
}
async onOpen() {
@@ -42,13 +45,21 @@ export class KhojChatModal extends Modal {
// Get chat history from Khoj backend
let getChatHistorySucessfully = await this.getChatHistory(chatBodyEl);
- let placeholderText = getChatHistorySucessfully ? "Chat with Khoj [Hit Enter to send message]" : "Configure Khoj to enable chat";
+ let placeholderText = getChatHistorySucessfully ? "Message" : "Configure Khoj to enable chat";
// Add chat input field
let inputRow = contentEl.createDiv("khoj-input-row");
- let chatInput = inputRow.createEl("input", {
+ let clearChat = inputRow.createEl("button", {
+ text: "Clear History",
+ attr: {
+ class: "khoj-input-row-button clickable-icon",
+ },
+ })
+ clearChat.addEventListener('click', async (_) => { await this.clearConversationHistory() });
+ setIcon(clearChat, "trash");
+
+ let chatInput = inputRow.createEl("textarea", {
attr: {
- type: "text",
id: "khoj-chat-input",
autofocus: "autofocus",
placeholder: placeholderText,
@@ -56,25 +67,32 @@ export class KhojChatModal extends Modal {
disabled: !getChatHistorySucessfully ? "disabled" : null
},
})
+ chatInput.addEventListener('input', (_) => { this.onChatInput() });
+ chatInput.addEventListener('keydown', (event) => { this.incrementalChat(event) });
let transcribe = inputRow.createEl("button", {
text: "Transcribe",
attr: {
id: "khoj-transcribe",
- class: "khoj-transcribe khoj-input-row-button",
+ class: "khoj-transcribe khoj-input-row-button clickable-icon ",
},
})
- transcribe.addEventListener('click', async (_) => { await this.speechToText() });
+ transcribe.addEventListener('mousedown', async (event) => { await this.speechToText(event) });
+ transcribe.addEventListener('touchstart', async (event) => { await this.speechToText(event) });
+ transcribe.addEventListener('touchend', async (event) => { await this.speechToText(event) });
+ transcribe.addEventListener('touchcancel', async (event) => { await this.speechToText(event) });
setIcon(transcribe, "mic");
- let clearChat = inputRow.createEl("button", {
- text: "Clear History",
+ let send = inputRow.createEl("button", {
+ text: "Send",
attr: {
- class: "khoj-input-row-button",
+ id: "khoj-chat-send",
+ class: "khoj-chat-send khoj-input-row-button clickable-icon",
},
})
- clearChat.addEventListener('click', async (_) => { await this.clearConversationHistory() });
- setIcon(clearChat, "trash");
+ setIcon(send, "arrow-up-circle");
+ let sendImg = send.getElementsByClassName("lucide-arrow-up-circle")[0]
+ sendImg.addEventListener('click', async (_) => { await this.chat() });
// Scroll to bottom of modal, till the send message input box
this.modalEl.scrollTop = this.modalEl.scrollHeight;
@@ -370,7 +388,7 @@ export class KhojChatModal extends Modal {
flashStatusInChatInput(message: string) {
// Get chat input element and original placeholder
- let chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ let chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
let originalPlaceholder = chatInput.placeholder;
// Set placeholder to message
chatInput.placeholder = message;
@@ -405,10 +423,13 @@ export class KhojChatModal extends Modal {
}
}
+ sendMessageTimeout: NodeJS.Timeout | undefined;
mediaRecorder: MediaRecorder | undefined;
- async speechToText() {
+ async speechToText(event: MouseEvent | TouchEvent) {
+ event.preventDefault();
const transcribeButton = this.contentEl.getElementsByClassName("khoj-transcribe")[0];
- const chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ const chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ const sendButton = this.modalEl.getElementsByClassName("khoj-chat-send")[0]
const generateRequestBody = async (audioBlob: Blob, boundary_string: string) => {
const boundary = `------${boundary_string}`;
@@ -439,7 +460,8 @@ export class KhojChatModal extends Modal {
// Parse response from Khoj backend
if (response.status === 200) {
console.log(response);
- chatInput.value += response.json.text;
+ chatInput.value += response.json.text.trimStart();
+ this.autoResize();
} else if (response.status === 501) {
throw new Error("⛔️ Configure speech-to-text model on server.");
} else if (response.status === 422) {
@@ -447,6 +469,28 @@ export class KhojChatModal extends Modal {
} else {
throw new Error("⛔️ Failed to transcribe audio.");
}
+
+ // Don't auto-send empty messages
+ if (chatInput.value.length === 0) return;
+
+ // Show stop auto-send button. It stops auto-send when clicked
+ setIcon(sendButton, "stop-circle");
+ let stopSendButtonImg = sendButton.getElementsByClassName("lucide-stop-circle")[0]
+ stopSendButtonImg.addEventListener('click', (_) => { this.cancelSendMessage() });
+
+ // Start the countdown timer UI
+ stopSendButtonImg.getElementsByTagName("circle")[0].style.animation = "countdown 3s linear 1 forwards";
+
+ // Auto send message after 3 seconds
+ this.sendMessageTimeout = setTimeout(() => {
+ // Stop the countdown timer UI
+ setIcon(sendButton, "arrow-up-circle")
+ let sendImg = sendButton.getElementsByClassName("lucide-arrow-up-circle")[0]
+ sendImg.addEventListener('click', async (_) => { await this.chat() });
+
+ // Send message
+ this.chat();
+ }, 3000);
};
const handleRecording = (stream: MediaStream) => {
@@ -468,18 +512,53 @@ export class KhojChatModal extends Modal {
};
// Toggle recording
- if (!this.mediaRecorder || this.mediaRecorder.state === 'inactive') {
+ if (!this.mediaRecorder || this.mediaRecorder.state === 'inactive' || event.type === 'touchstart') {
navigator.mediaDevices
.getUserMedia({ audio: true })
- .then(handleRecording)
+ ?.then(handleRecording)
.catch((e) => {
this.flashStatusInChatInput("⛔️ Failed to access microphone");
});
- } else if (this.mediaRecorder.state === 'recording') {
+ } else if (this.mediaRecorder.state === 'recording' || event.type === 'touchend' || event.type === 'touchcancel') {
this.mediaRecorder.stop();
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
this.mediaRecorder = undefined;
setIcon(transcribeButton, "mic");
}
}
+
+ cancelSendMessage() {
+ // Cancel the auto-send chat message timer if the stop-send-button is clicked
+ clearTimeout(this.sendMessageTimeout);
+
+ // Revert to showing send-button and hide the stop-send-button
+ let sendButton = this.modalEl.getElementsByClassName("khoj-chat-send")[0];
+ setIcon(sendButton, "arrow-up-circle");
+ let sendImg = sendButton.getElementsByClassName("lucide-arrow-up-circle")[0]
+ sendImg.addEventListener('click', async (_) => { await this.chat() });
+ };
+
+ incrementalChat(event: KeyboardEvent) {
+ if (!event.shiftKey && event.key === 'Enter') {
+ event.preventDefault();
+ this.chat();
+ }
+ }
+
+ onChatInput() {
+ const chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ chatInput.value = chatInput.value.trimStart();
+
+ this.autoResize();
+ }
+
+ autoResize() {
+ const chatInput = this.contentEl.getElementsByClassName("khoj-chat-input")[0];
+ const scrollTop = chatInput.scrollTop;
+ chatInput.style.height = '0';
+ const scrollHeight = chatInput.scrollHeight + 8; // +8 accounts for padding
+ chatInput.style.height = Math.min(scrollHeight, 200) + 'px';
+ chatInput.scrollTop = scrollTop;
+ this.modalEl.scrollTop = this.modalEl.scrollHeight;
+ }
}
diff --git a/src/interface/obsidian/styles.css b/src/interface/obsidian/styles.css
index 11fc0086..3250ecc8 100644
--- a/src/interface/obsidian/styles.css
+++ b/src/interface/obsidian/styles.css
@@ -230,36 +230,58 @@ img {
}
.khoj-input-row {
display: grid;
- grid-template-columns: auto 32px 32px;
+ grid-template-columns: 32px auto 32px 32px;
grid-column-gap: 10px;
grid-row-gap: 10px;
background: var(--background-primary);
+ margin: 0 0 0 -8px;
+ align-items: center;
}
#khoj-chat-input.option:hover {
box-shadow: 0 0 11px var(--background-modifier-box-shadow);
}
#khoj-chat-input {
font-size: var(--font-ui-medium);
- padding: 25px 20px;
+ padding: 4px 0 0 12px;
+ border-radius: 16px;
+ height: 32px;
+ resize: none;
}
.khoj-input-row-button {
- background: var(--background-primary);
- border: none;
- border-radius: 5px;
- padding: 5px;
+ border-radius: 50%;
+ padding: 4px;
--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;
+ height: 32px;
+ width: 32px;
}
-.khoj-input-row-button:hover {
- background: var(--background-modifier-hover);
+
+#khoj-chat-send {
+ padding: 0;
+ position: relative;
}
-.khoj-input-row-button:active {
- background: var(--background-modifier-active);
+#khoj-chat-send .lucide-arrow-up-circle {
+ background: var(--khoj-sun);
+ border-radius: 50%;
+ color: #222;
+}
+#khoj-chat-send .lucide-stop-circle {
+ transform: rotateY(-180deg) rotateZ(-90deg);
+}
+#khoj-chat-send .lucide-stop-circle circle {
+ stroke-dasharray: 62px; /* The circumference of the circle with 7px radius */
+ stroke-dashoffset: 0px;
+ stroke-linecap: round;
+ stroke-width: 2px;
+ stroke: var(--main-text-color);
+ fill: none;
+}
+@keyframes countdown {
+ from {
+ stroke-dashoffset: 0px;
+ }
+ to {
+ stroke-dashoffset: -62px; /* The circumference of the circle with 7px radius */
+ }
}
@media (pointer: coarse), (hover: none) {
diff --git a/src/khoj/interface/web/chat.html b/src/khoj/interface/web/chat.html
index 2a813779..19853be5 100644
--- a/src/khoj/interface/web/chat.html
+++ b/src/khoj/interface/web/chat.html
@@ -528,7 +528,7 @@ To get started, just start typing below. You can also type / to see a list of co
const textarea = document.getElementById('chat-input');
const scrollTop = textarea.scrollTop;
textarea.style.height = '0';
- const scrollHeight = textarea.scrollHeight;
+ const scrollHeight = textarea.scrollHeight + 8; // +8 accounts for padding
textarea.style.height = Math.min(scrollHeight, 200) + 'px';
textarea.scrollTop = scrollTop;
document.getElementById("chat-body").scrollTop = document.getElementById("chat-body").scrollHeight;
@@ -581,7 +581,7 @@ To get started, just start typing below. You can also type / to see a list of co
.then(response => response.json())
.then(data => {
// Render chat options, if any
- if (data) {
+ if (data.length > 0) {
let questionStarterSuggestions = document.getElementById("question-starters");
for (let index in data) {
let questionStarter = data[index];
@@ -639,9 +639,14 @@ To get started, just start typing below. You can also type / to see a list of co
});
}
+ let sendMessageTimeout;
let mediaRecorder;
- function speechToText() {
+ function speechToText(event) {
+ event.preventDefault();
const speakButtonImg = document.getElementById('speak-button-img');
+ const stopRecordButtonImg = document.getElementById('stop-record-button-img');
+ const sendButtonImg = document.getElementById('send-button-img');
+ const stopSendButtonImg = document.getElementById('stop-send-button-img');
const chatInput = document.getElementById('chat-input');
const sendToServer = (audioBlob) => {
@@ -650,7 +655,30 @@ To get started, just start typing below. You can also type / to see a list of co
fetch('/api/transcribe?client=web', { method: 'POST', body: formData })
.then(response => response.ok ? response.json() : Promise.reject(response))
- .then(data => { chatInput.value += data.text; })
+ .then(data => { chatInput.value += data.text.trimStart(); autoResize(); })
+ .then(() => {
+ // Don't auto-send empty messages
+ if (chatInput.value.length === 0) return;
+
+ // Send message after 3 seconds, unless stop send button is clicked
+ sendButtonImg.style.display = 'none';
+ stopSendButtonImg.style.display = 'initial';
+
+ // Start the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "countdown 3s linear 1 forwards";
+
+ sendMessageTimeout = setTimeout(() => {
+ // Revert to showing send-button and hide the stop-send-button
+ sendButtonImg.style.display = 'initial';
+ stopSendButtonImg.style.display = 'none';
+
+ // Stop the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "none";
+
+ // Send message
+ chat();
+ }, 3000);
+ })
.catch(err => {
if (err.status === 501) {
flashStatusInChatInput("⛔️ Configure speech-to-text model on server.")
@@ -679,26 +707,38 @@ To get started, just start typing below. You can also type / to see a list of co
});
mediaRecorder.start();
- speakButtonImg.src = '/static/assets/icons/stop-solid.svg';
- speakButtonImg.alt = 'Stop Transcription';
+ speakButtonImg.style.display = 'none';
+ stopRecordButtonImg.style.display = 'initial';
};
// Toggle recording
- if (!mediaRecorder || mediaRecorder.state === 'inactive') {
+ if (!mediaRecorder || mediaRecorder.state === 'inactive' || event.type === 'touchstart') {
navigator.mediaDevices
- .getUserMedia({ audio: true })
+ ?.getUserMedia({ audio: true })
.then(handleRecording)
.catch((e) => {
flashStatusInChatInput("⛔️ Failed to access microphone");
});
- } else if (mediaRecorder.state === 'recording') {
+ } else if (mediaRecorder.state === 'recording' || event.type === 'touchend' || event.type === 'touchcancel') {
mediaRecorder.stop();
mediaRecorder.stream.getTracks().forEach(track => track.stop());
mediaRecorder = null;
- speakButtonImg.src = '/static/assets/icons/microphone-solid.svg';
- speakButtonImg.alt = 'Transcribe';
+ speakButtonImg.style.display = 'initial';
+ stopRecordButtonImg.style.display = 'none';
}
}
+
+ function cancelSendMessage() {
+ // Cancel the chat() call if the stop-send-button is clicked
+ clearTimeout(sendMessageTimeout);
+
+ // Revert to showing send-button and hide the stop-send-button
+ document.getElementById('stop-send-button-img').style.display = 'none';
+ document.getElementById('send-button-img').style.display = 'initial';
+
+ // Stop the countdown timer UI
+ document.getElementById('countdown-circle').style.animation = "none";
+ };
@@ -718,12 +758,36 @@ To get started, just start typing below. You can also type / to see a list of co
@@ -803,6 +867,9 @@ To get started, just start typing below. You can also type / to see a list of co
transition: max-height 0.3s ease-in-out;
overflow: hidden;
}
+ button.question-starter:hover {
+ background: var(--primary-hover);
+ }
button.reference-button {
background: var(--background-color);
@@ -963,10 +1030,11 @@ To get started, just start typing below. You can also type / to see a list of co
}
#input-row {
display: grid;
- grid-template-columns: auto 32px 32px;
+ grid-template-columns: 32px auto 32px 40px;
grid-column-gap: 10px;
grid-row-gap: 10px;
background: var(--background-color);
+ align-items: center;
}
.option:hover {
box-shadow: 0 0 11px #aaa;
@@ -974,12 +1042,13 @@ To get started, just start typing below. You can also type / to see a list of co
#chat-input {
font-family: roboto, karma, segoe ui, sans-serif;
font-size: medium;
- height: 54px;
+ height: 36px;
+ border-radius: 16px;
resize: none;
overflow-y: hidden;
max-height: 200px;
box-sizing: border-box;
- padding: 15px;
+ padding: 4px 0 0 12px;
line-height: 1.5em;
margin: 0;
}
@@ -988,15 +1057,18 @@ To get started, just start typing below. You can also type / to see a list of co
}
.input-row-button {
background: var(--background-color);
- border: 1px solid var(--main-text-color);
- box-shadow: 0 0 11px #aaa;
- border-radius: 5px;
- padding: 0px;
+ border: none;
+ box-shadow: none;
+ border-radius: 50%;
font-size: 14px;
font-weight: 300;
line-height: 1.5em;
cursor: pointer;
transition: background 0.3s ease-in-out;
+ width: 40px;
+ height: 40px;
+ margin-top: -2px;
+ margin-left: -5px;
}
.input-row-button:hover {
background: var(--primary-hover);
@@ -1006,8 +1078,43 @@ To get started, just start typing below. You can also type / to see a list of co
}
.input-row-button-img {
width: 24px;
+ height: 24px;
+ }
+ #send-button {
+ padding: 0;
+ position: relative;
+ }
+ #send-button-img {
+ width: 28px;
+ height: 28px;
+ background: var(--primary-hover);
+ border-radius: 50%;
}
+ #stop-send-button-img {
+ position: absolute;
+ top: 6px;
+ right: 6px;
+ width: 28px;
+ height: 28px;
+ transform: rotateY(-180deg) rotateZ(-90deg);
+ }
+ #countdown-circle {
+ stroke-dasharray: 44px; /* The circumference of the circle with 7px radius */
+ stroke-dashoffset: 0px;
+ stroke-linecap: round;
+ stroke-width: 1px;
+ stroke: var(--main-text-color);
+ fill: none;
+ }
+ @keyframes countdown {
+ from {
+ stroke-dashoffset: 0px;
+ }
+ to {
+ stroke-dashoffset: -44px; /* The circumference of the circle with 7px radius */
+ }
+ }
.option-enabled {
box-shadow: 0 0 12px rgb(119, 156, 46);
@@ -1083,6 +1190,9 @@ To get started, just start typing below. You can also type / to see a list of co
img.text-to-image {
max-width: 100%;
}
+ #clear-chat-button {
+ margin-left: 0;
+ }
}
@media only screen and (min-width: 700px) {
body {