mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19:12 +00:00
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:
@@ -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();
|
||||
|
||||
@@ -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] {
|
||||
|
||||
Reference in New Issue
Block a user