Add stop button to cancel in-flight requests

Swap the Send button for a Stop button while a response is streaming, letting users abort the current operation cleanly.
Cancelled requests now display "Stopped." instead of an error message.

Also includes two smaller fixes: preserve the original assistant text and full tool_calls array when executing write
tools for follow-up context, and add 'research' to agentic modes.
This commit is contained in:
2026-05-21 18:30:43 +02:00
parent 3d9be7dfdb
commit d330b94816
3 changed files with 137 additions and 34 deletions
+49 -14
View File
@@ -10914,6 +10914,7 @@ var ChatView = class extends import_obsidian5.ItemView {
this.lastMessageEl = null;
this.newChatButton = null;
this.sendButton = null;
this.stopButton = null;
this.inputEl = null;
this.chatContainer = null;
this.sendButtonClickHandler = null;
@@ -10923,6 +10924,7 @@ var ChatView = class extends import_obsidian5.ItemView {
this.inputKeyDownWrapper = null;
this.newChatButtonClickWrapper = null;
this.listenersAttached = false;
this.isCancelled = false;
this.modeSelectorEl = null;
this.modelSelectorEl = null;
this.historySelectEl = null;
@@ -11074,6 +11076,7 @@ var ChatView = class extends import_obsidian5.ItemView {
}
this.lastMessageEl = null;
this.sendButton = null;
this.stopButton = null;
this.inputEl = null;
this.chatContainer = null;
this.showLogsButton = null;
@@ -11268,6 +11271,17 @@ var ChatView = class extends import_obsidian5.ItemView {
} else {
inputContainer.appendChild(this.sendButton);
}
if (!this.stopButton) {
this.stopButton = inputContainer.createEl("button", {
cls: "ollama-stop-button",
text: "Stop"
});
this.stopButton.addEventListener("click", () => {
this.cancelCurrentOperation();
});
}
inputContainer.appendChild(this.stopButton);
this.stopButton.style.display = "none";
if (this.contentEl.addClass) {
this.contentEl.addClass("ollama-chat-view-content");
} else {
@@ -12279,15 +12293,25 @@ ${actualMessage}` : actualMessage;
this.render();
this.syncMessagesToSession();
} catch (error) {
ErrorHandler.handleError(error, "ChatView.handleUserInput");
const errorMsg = error instanceof Error ? error.message : String(error);
this.updateMessageById(assistantMessageId, {
content: `An error occurred: ${errorMsg}`,
isStreaming: false,
isThinking: false
});
this.syncMessagesToSession();
if (this.isCancelled) {
this.updateMessageById(assistantMessageId, {
content: "Stopped.",
isStreaming: false,
isThinking: false
});
this.syncMessagesToSession();
} else {
ErrorHandler.handleError(error, "ChatView.handleUserInput");
const errorMsg = error instanceof Error ? error.message : String(error);
this.updateMessageById(assistantMessageId, {
content: `An error occurred: ${errorMsg}`,
isStreaming: false,
isThinking: false
});
this.syncMessagesToSession();
}
} finally {
this.isCancelled = false;
this.hideActivityIndicator();
this.cleanupStreamingResources();
}
@@ -12407,9 +12431,11 @@ ${actualMessage}` : actualMessage;
textEl.textContent = text;
}
this.activityIndicatorEl.style.display = "flex";
if (this.sendButton && this.sendButton instanceof HTMLButtonElement) {
this.sendButton.disabled = true;
this.sendButton.textContent = "Working\u2026";
if (this.sendButton) {
this.sendButton.style.display = "none";
}
if (this.stopButton) {
this.stopButton.style.display = "inline-flex";
}
if (this.inputEl) {
this.inputEl.disabled = true;
@@ -12418,14 +12444,23 @@ ${actualMessage}` : actualMessage;
hideActivityIndicator() {
if (!this.activityIndicatorEl) return;
this.activityIndicatorEl.style.display = "none";
if (this.sendButton && this.sendButton instanceof HTMLButtonElement) {
this.sendButton.disabled = false;
this.sendButton.textContent = "Send";
if (this.sendButton) {
this.sendButton.style.display = "inline-flex";
}
if (this.stopButton) {
this.stopButton.style.display = "none";
}
if (this.inputEl) {
this.inputEl.disabled = false;
this.inputEl.focus();
}
}
cancelCurrentOperation() {
this.isCancelled = true;
this.ollamaClient.cancelStream();
this.agentOllamaClient.cancelStream();
new import_obsidian5.Notice("Stopping\u2026");
}
createOllamaClient(model, settings) {
return new OllamaClient(settings.ollamaUrl, model, void 0, settings.cacheConfig);
}