mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-09 05:39:12 +00:00
Move to single click audio chat UX on desktop app
- 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:
@@ -673,10 +673,14 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sendMessageTimeout;
|
||||||
let mediaRecorder;
|
let mediaRecorder;
|
||||||
async function speechToText() {
|
async function speechToText(event) {
|
||||||
|
event.preventDefault();
|
||||||
const speakButtonImg = document.getElementById('speak-button-img');
|
const speakButtonImg = document.getElementById('speak-button-img');
|
||||||
const stopRecordButtonImg = document.getElementById('stop-record-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 chatInput = document.getElementById('chat-input');
|
||||||
|
|
||||||
const hostURL = await window.hostURLAPI.getURL();
|
const hostURL = await window.hostURLAPI.getURL();
|
||||||
@@ -691,6 +695,29 @@
|
|||||||
fetch(url, { method: 'POST', body: formData, headers})
|
fetch(url, { method: 'POST', body: formData, headers})
|
||||||
.then(response => response.ok ? response.json() : Promise.reject(response))
|
.then(response => response.ok ? response.json() : Promise.reject(response))
|
||||||
.then(data => { chatInput.value += data.text.trimStart(); autoResize(); })
|
.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 => {
|
.catch(err => {
|
||||||
if (err.status === 501) {
|
if (err.status === 501) {
|
||||||
flashStatusInChatInput("⛔️ Configure speech-to-text model on server.")
|
flashStatusInChatInput("⛔️ Configure speech-to-text model on server.")
|
||||||
@@ -738,6 +765,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<body>
|
<body>
|
||||||
<div id="khoj-empty-container" class="khoj-empty-container">
|
<div id="khoj-empty-container" class="khoj-empty-container">
|
||||||
@@ -776,7 +814,8 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<textarea id="chat-input" class="option" oninput="onChatInput()" onkeydown=incrementalChat(event) autofocus="autofocus" placeholder="Message"></textarea>
|
<textarea id="chat-input" class="option" oninput="onChatInput()" onkeydown=incrementalChat(event) autofocus="autofocus" placeholder="Message"></textarea>
|
||||||
<button id="speak-button" class="input-row-button" onclick="speechToText()">
|
<button id="speak-button" class="input-row-button"
|
||||||
|
ontouchstart="speechToText(event)" ontouchend="speechToText(event)" onmousedown="speechToText(event)" onmouseup="speechToText(event)">
|
||||||
<svg id="speak-button-img" class="input-row-button-img" alt="Transcribe" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
<svg id="speak-button-img" class="input-row-button-img" alt="Transcribe" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z"/>
|
<path d="M3.5 6.5A.5.5 0 0 1 4 7v1a4 4 0 0 0 8 0V7a.5.5 0 0 1 1 0v1a5 5 0 0 1-4.5 4.975V15h3a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1h3v-2.025A5 5 0 0 1 3 8V7a.5.5 0 0 1 .5-.5z"/>
|
||||||
<path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0v5zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3z"/>
|
<path d="M10 8a2 2 0 1 1-4 0V3a2 2 0 1 1 4 0v5zM8 0a3 3 0 0 0-3 3v5a3 3 0 0 0 6 0V3a3 3 0 0 0-3-3z"/>
|
||||||
@@ -786,10 +825,14 @@
|
|||||||
<path d="M5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3z"/>
|
<path d="M5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button id="send-button" class="input-row-button" onclick="chat()" alt="Send message" type="submit" >
|
<button id="send-button" class="input-row-button" alt="Send message">
|
||||||
<svg id="send-button-img" class="input-row-button-img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
<svg id="send-button-img" onclick="chat()" class="input-row-button-img" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||||
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-7.5 3.5a.5.5 0 0 1-1 0V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11.5z"/>
|
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-7.5 3.5a.5.5 0 0 1-1 0V5.707L5.354 7.854a.5.5 0 1 1-.708-.708l3-3a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1-.708.708L8.5 5.707V11.5z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<svg id="stop-send-button-img" onclick="cancelSendMessage()" style="display: none" class="input-row-button-img" alt="Stop Message Send" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<circle id="countdown-circle" class="countdown-circle" cx="8" cy="8" r="7" />
|
||||||
|
<path d="M5 6.5A1.5 1.5 0 0 1 6.5 5h3A1.5 1.5 0 0 1 11 6.5v3A1.5 1.5 0 0 1 9.5 11h-3A1.5 1.5 0 0 1 5 9.5v-3z"/>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -966,8 +1009,8 @@
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
#send-button {
|
#send-button {
|
||||||
padding-top: 0;
|
padding: 0;
|
||||||
padding-right: 3px;
|
position: relative;
|
||||||
}
|
}
|
||||||
#send-button-img {
|
#send-button-img {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
@@ -975,6 +1018,30 @@
|
|||||||
background: var(--primary-hover);
|
background: var(--primary-hover);
|
||||||
border-radius: 50%;
|
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 {
|
.option-enabled {
|
||||||
box-shadow: 0 0 12px rgb(119, 156, 46);
|
box-shadow: 0 0 12px rgb(119, 156, 46);
|
||||||
|
|||||||
Reference in New Issue
Block a user