make some overall QoL improvements
This commit is contained in:
@@ -7,16 +7,17 @@ export function createComposerController({
|
||||
state, elements, attachmentComposer, webSearchButton, reasoningToggle, webSearchToggle,
|
||||
setChatActivity, updateComposerPlaceholder, setNotice, createNewChat, getChatById,
|
||||
updateWorkspaceCopy, renderChatList, addMessage, updateChatActionState, scrollMessagesToBottom,
|
||||
setMessageContent, setActiveChat, loadSummary, markOnboardingCompleted, maybeShowPersonaNudge,
|
||||
setMessageContent, announceResponding, announceReply, announceResponseStatus, setActiveChat, loadSummary, markOnboardingCompleted, maybeShowPersonaNudge,
|
||||
api = defaultApi
|
||||
}) {
|
||||
const {msgInput, sendBtn, messagesDiv, modelSelect} = elements;
|
||||
const {msgInput, sendBtn, stopBtn, messagesDiv, modelSelect} = elements;
|
||||
const composerHint = document.getElementById("composerHint");
|
||||
let sendController = null;
|
||||
|
||||
function updateSendState() {
|
||||
const hasText = Boolean(msgInput.value.trim());
|
||||
sendBtn.disabled = state.isProcessing ? false : attachmentComposer.pending || (!hasText && !attachmentComposer.attachments.length);
|
||||
sendBtn.setAttribute("aria-label", state.isProcessing ? "Stop generation" : "Send message");
|
||||
sendBtn.disabled = state.isProcessing || attachmentComposer.pending || (!hasText && !attachmentComposer.attachments.length);
|
||||
stopBtn.hidden = !state.isProcessing;
|
||||
}
|
||||
|
||||
function resizeComposerInput() {
|
||||
@@ -26,21 +27,22 @@ export function createComposerController({
|
||||
|
||||
function setLoadingState(loading, overlayOptions = null) {
|
||||
state.isProcessing = loading;
|
||||
msgInput.disabled = loading;
|
||||
webSearchButton.disabled = loading;
|
||||
reasoningToggle.setBusy(loading);
|
||||
attachmentComposer.setBusy(loading);
|
||||
sendBtn.classList.toggle("loading", loading);
|
||||
messagesDiv.querySelectorAll('[data-message-action="regenerate"]').forEach(button => {
|
||||
button.disabled = loading || button.dataset.retryLimitReached === "true";
|
||||
});
|
||||
messagesDiv.querySelectorAll('[data-message-action="retry-failed"]').forEach(button => { button.disabled = loading; });
|
||||
if (loading) {
|
||||
msgInput.placeholder = "Processing response...";
|
||||
msgInput.placeholder = "Draft your next message...";
|
||||
composerHint.textContent = "Draft your next message · Enter for new line";
|
||||
announceResponding();
|
||||
if (overlayOptions !== false) setChatActivity(true, overlayOptions || undefined);
|
||||
} else {
|
||||
setChatActivity(false);
|
||||
updateComposerPlaceholder();
|
||||
composerHint.textContent = "Enter to send · Shift+Enter for new line";
|
||||
}
|
||||
updateSendState();
|
||||
}
|
||||
@@ -76,16 +78,16 @@ export function createComposerController({
|
||||
let userCard, loadingMsg, rendering, completedReply = null;
|
||||
let completedThinking = "", completedSources = null;
|
||||
const userMessage = {role: "user", content: message, attachments};
|
||||
if (!retryMessage) {
|
||||
msgInput.value = "";
|
||||
state.chatDrafts.set(chatId ?? "new", "");
|
||||
resizeComposerInput();
|
||||
}
|
||||
const clearSentDraft = () => {
|
||||
if (state.chatDrafts.get(chatId)?.trim() === message) state.chatDrafts.delete(chatId);
|
||||
attachmentComposer.clearSentDraft(chatId, attachments.map(item => item.id));
|
||||
};
|
||||
try {
|
||||
setLoadingState(true, false);
|
||||
if (retryMessage && !msgInput.value.trim()) {
|
||||
msgInput.value = message;
|
||||
resizeComposerInput();
|
||||
}
|
||||
if (!chatId) {
|
||||
const created = await createNewChat({preserveComposer: true, signal});
|
||||
signal.throwIfAborted();
|
||||
@@ -152,6 +154,7 @@ export function createComposerController({
|
||||
state.currentMessages.push(reply);
|
||||
if (loadingMsg?.isConnected) loadingMsg.replaceWith(addMessage(reply, false, false, {detached: true}));
|
||||
else addMessage(reply);
|
||||
announceReply(completedReply);
|
||||
} else loadingMsg?.remove();
|
||||
reasoningToggle.reset();
|
||||
if (visible()) {
|
||||
@@ -160,7 +163,6 @@ export function createComposerController({
|
||||
const refreshed = await setActiveChat(chatId);
|
||||
if (refreshed?.error) throw new Error(refreshed.error);
|
||||
if (visible()) {
|
||||
if (msgInput.value.trim() === message) { msgInput.value = ""; resizeComposerInput(); }
|
||||
if (msgInput.value) state.chatDrafts.set(chatId, msgInput.value);
|
||||
else state.chatDrafts.delete(chatId);
|
||||
const currentAttachments = attachmentComposer.attachments;
|
||||
@@ -178,11 +180,16 @@ export function createComposerController({
|
||||
} catch (error) {
|
||||
loadingMsg?.remove();
|
||||
const messageText = signal.aborted ? "Generation stopped." : error?.message || "Unable to send message.";
|
||||
if (visible() && completedReply === null) announceResponseStatus(messageText);
|
||||
if (completedReply !== null) {
|
||||
setNotice(`Reply sent, but the conversation could not be refreshed: ${messageText}`, "error");
|
||||
} else {
|
||||
if (visible()) {
|
||||
if (!msgInput.value.trim()) { msgInput.value = message; resizeComposerInput(); }
|
||||
if (!msgInput.value.trim()) {
|
||||
msgInput.value = message;
|
||||
state.chatDrafts.set(state.activeChatId ?? "new", message);
|
||||
resizeComposerInput();
|
||||
}
|
||||
userMessage.deliveryStatus = "failed";
|
||||
userMessage.errorMessage = messageText;
|
||||
if (userCard?.isConnected) userCard.replaceWith(addMessage(userMessage, true, false, {detached: true}));
|
||||
@@ -193,7 +200,7 @@ export function createComposerController({
|
||||
rendering?.dispose();
|
||||
sendController = null;
|
||||
state.sendingChatId = null;
|
||||
setLoadingState(false); msgInput.focus(); updateChatActionState();
|
||||
setLoadingState(false); updateChatActionState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user