Move to single click audio chat UX on Obsidian client

- Capabillity
  New default UX has 1 long-press to send transcribed audio message

  - Removes the previous default of 3 clicks required to send audio message
    - The record > stop > send process to send audio messages was unclear
  - Still allows stopping message from being sent, if users want to make
    correction to transcribed audio
  - Removes inadvertent long audio transcriptions if user forgets to
    press stop when recording

- Changes
  - Record audio while microphone button pressed
  - Show auto-send 3s countdown timer UI for audio chat message
    Provide a visual cue around send button for how long before audio
    message is automatically sent to Khoj for response
  - Auto-send msg in 3s unless stop send message button clicked
This commit is contained in:
Debanjum Singh Solanky
2024-01-20 15:12:06 +05:30
parent 29a581d2b0
commit f0daa45ae0
2 changed files with 69 additions and 5 deletions

View File

@@ -77,18 +77,22 @@ export class KhojChatModal extends Modal {
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('mouseup', async (event) => { await this.speechToText(event) });
transcribe.addEventListener('touchstart', async (event) => { await this.speechToText(event) });
transcribe.addEventListener('touchend', async (event) => { await this.speechToText(event) });
setIcon(transcribe, "mic");
let send = inputRow.createEl("button", {
text: "Send",
attr: {
id: "khoj-chat-send",
class: "khoj-input-row-button clickable-icon",
class: "khoj-chat-send khoj-input-row-button clickable-icon",
},
})
send.addEventListener('click', async (_) => { await this.chat() });
setIcon(send, "arrow-up-circle");
let sendImg = <SVGElement>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;
@@ -419,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 = <HTMLButtonElement>this.contentEl.getElementsByClassName("khoj-transcribe")[0];
const chatInput = <HTMLTextAreaElement>this.contentEl.getElementsByClassName("khoj-chat-input")[0];
const sendButton = <HTMLButtonElement>this.modalEl.getElementsByClassName("khoj-chat-send")[0]
const generateRequestBody = async (audioBlob: Blob, boundary_string: string) => {
const boundary = `------${boundary_string}`;
@@ -462,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 = <SVGElement>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 = <SVGElement>sendButton.getElementsByClassName("lucide-arrow-up-circle")[0]
sendImg.addEventListener('click', async (_) => { await this.chat() });
// Send message
this.chat();
}, 3000);
};
const handleRecording = (stream: MediaStream) => {
@@ -498,6 +527,17 @@ export class KhojChatModal extends Modal {
}
}
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 = <HTMLButtonElement>this.modalEl.getElementsByClassName("khoj-chat-send")[0];
setIcon(sendButton, "arrow-up-circle");
let sendImg = <SVGElement>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();

View File

@@ -254,11 +254,35 @@ img {
height: 32px;
width: 32px;
}
#khoj-chat-send .svg-icon {
#khoj-chat-send {
padding: 0;
position: relative;
}
#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) {
#khoj-chat-body.abbr[title] {