make some overall QoL improvements
CI / check (22) (push) Failing after 16m26s
CI / check (24) (push) Failing after 15m50s

This commit is contained in:
Frederik Spirgi
2026-10-08 11:02:32 +02:00
parent 0f58a996ec
commit 8666e09049
7 changed files with 271 additions and 50 deletions
+22 -15
View File
@@ -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();
}
}