```
Increase test coverage across multiple modules (91.2% statements, 82.85% branches, 82.35% functions, 93.1% lines) Update abort controller handling in OllamaClient to properly clean up previous controllers Remove unused imports and constants: MODEL_NAME_REGEX, MouseEvent, DEFAULT_SETTINGS, convertMarkdownToHtml Refactor event handler naming to be more consistent ```
This commit is contained in:
+38
-51
@@ -4,7 +4,6 @@ import { ItemView, WorkspaceLeaf, Notice } from 'obsidian';
|
||||
type KeyboardEvent = globalThis.KeyboardEvent;
|
||||
type HTMLTextAreaElement = globalThis.HTMLTextAreaElement;
|
||||
type HTMLButtonElement = globalThis.HTMLButtonElement;
|
||||
type MouseEvent = globalThis.MouseEvent;
|
||||
|
||||
const DEFAULT_VAULT_SEARCH_LIMIT = 3;
|
||||
const MAX_MESSAGE_HISTORY = 50;
|
||||
@@ -29,16 +28,16 @@ export class ChatView extends ItemView {
|
||||
private vaultIndexer: VaultIndexer;
|
||||
private toolExecutor: ToolExecutor;
|
||||
private lastMessageEl: HTMLElement | null = null;
|
||||
private newChatButton: HTMLElement | null = null;
|
||||
private sendButton: HTMLElement | null = null;
|
||||
private inputEl: HTMLElement | null = null;
|
||||
private newChatButton: HTMLButtonElement | null = null;
|
||||
private sendButton: HTMLButtonElement | null = null;
|
||||
private inputEl: HTMLTextAreaElement | null = null;
|
||||
private chatContainer: HTMLElement | null = null;
|
||||
private sendButtonClickHandler: (() => Promise<void>) | null = null;
|
||||
private inputKeyDownHandler: ((e: KeyboardEvent) => Promise<void>) | null = null;
|
||||
private newChatButtonClickHandler: (() => void) | null = null;
|
||||
private sendButtonEventHandler: ((e: MouseEvent) => void) | null = null;
|
||||
private inputKeyDownEventHandler: ((e: KeyboardEvent) => void) | null = null;
|
||||
private newChatButtonEventHandler: ((e: MouseEvent) => void) | null = null;
|
||||
private sendButtonClickWrapper: (() => void) | null = null;
|
||||
private inputKeyDownWrapper: ((e: KeyboardEvent) => void) | null = null;
|
||||
private newChatButtonClickWrapper: (() => void) | null = null;
|
||||
private listenersAttached = false;
|
||||
|
||||
// Getters for testing
|
||||
@@ -98,8 +97,9 @@ export class ChatView extends ItemView {
|
||||
}
|
||||
|
||||
private cleanupStreamingResources(): void {
|
||||
// Ensure any ongoing streaming is properly cleaned up
|
||||
if (this.lastMessageEl && this.lastMessageEl.parentElement) {
|
||||
// Only cleanup if there's still an active streaming message
|
||||
const streamingMessage = this.messages.find((msg) => msg.isStreaming);
|
||||
if (streamingMessage && this.lastMessageEl && this.lastMessageEl.parentElement) {
|
||||
this.lastMessageEl.parentElement.removeChild(this.lastMessageEl);
|
||||
this.lastMessageEl = null;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ export class ChatView extends ItemView {
|
||||
this.sendButton = inputContainer.createEl('button', {
|
||||
cls: 'ollama-send-button',
|
||||
});
|
||||
(this.sendButton as HTMLButtonElement).textContent = 'Send';
|
||||
this.sendButton.textContent = 'Send';
|
||||
}
|
||||
|
||||
if (!this.newChatButton) {
|
||||
@@ -130,8 +130,8 @@ export class ChatView extends ItemView {
|
||||
this.newChatButton = newChatContainer.createEl('button', {
|
||||
cls: 'ollama-new-chat-button',
|
||||
});
|
||||
(this.newChatButton as HTMLButtonElement).textContent = '🔄 New Chat';
|
||||
(this.newChatButton as HTMLButtonElement).title = 'Start a new conversation';
|
||||
this.newChatButton.textContent = '🔄 New Chat';
|
||||
this.newChatButton.title = 'Start a new conversation';
|
||||
}
|
||||
|
||||
// Create immutable snapshot for rendering
|
||||
@@ -183,8 +183,8 @@ export class ChatView extends ItemView {
|
||||
if (!this.sendButtonClickHandler) {
|
||||
this.sendButtonClickHandler = async () => {
|
||||
if (!this.inputEl) return;
|
||||
await this.handleUserInput((this.inputEl as HTMLTextAreaElement).value);
|
||||
(this.inputEl as HTMLTextAreaElement).value = '';
|
||||
await this.handleUserInput(this.inputEl.value);
|
||||
this.inputEl.value = '';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -192,60 +192,47 @@ export class ChatView extends ItemView {
|
||||
this.inputKeyDownHandler = async (e: KeyboardEvent) => {
|
||||
if (!this.inputEl || e.key !== 'Enter' || e.shiftKey) return;
|
||||
e.preventDefault();
|
||||
await this.handleUserInput((this.inputEl as HTMLTextAreaElement).value);
|
||||
(this.inputEl as HTMLTextAreaElement).value = '';
|
||||
await this.handleUserInput(this.inputEl.value);
|
||||
this.inputEl.value = '';
|
||||
};
|
||||
}
|
||||
|
||||
// Add event listeners
|
||||
this.sendButtonEventHandler = () => {
|
||||
// Create wrapper functions for event listeners
|
||||
this.sendButtonClickWrapper = () => {
|
||||
void this.sendButtonClickHandler?.();
|
||||
};
|
||||
this.inputKeyDownEventHandler = (e: KeyboardEvent) => {
|
||||
this.inputKeyDownWrapper = (e: KeyboardEvent) => {
|
||||
void this.inputKeyDownHandler?.(e);
|
||||
};
|
||||
(this.sendButton as HTMLButtonElement).addEventListener('click', this.sendButtonEventHandler);
|
||||
(this.inputEl as HTMLTextAreaElement).addEventListener(
|
||||
'keydown',
|
||||
this.inputKeyDownEventHandler
|
||||
);
|
||||
this.newChatButtonClickWrapper = () => {
|
||||
void this.newChatButtonClickHandler?.();
|
||||
};
|
||||
|
||||
// Add event listeners using wrappers
|
||||
this.sendButton.addEventListener('click', this.sendButtonClickWrapper);
|
||||
this.inputEl.addEventListener('keydown', this.inputKeyDownWrapper);
|
||||
if (this.newChatButton) {
|
||||
if (!this.newChatButtonClickHandler) {
|
||||
this.newChatButtonClickHandler = () => this.clearConversation();
|
||||
}
|
||||
this.newChatButtonEventHandler = () => {
|
||||
this.newChatButtonClickHandler?.();
|
||||
};
|
||||
(this.newChatButton as HTMLButtonElement).addEventListener(
|
||||
'click',
|
||||
this.newChatButtonEventHandler
|
||||
);
|
||||
this.newChatButton.addEventListener('click', this.newChatButtonClickWrapper);
|
||||
}
|
||||
this.listenersAttached = true;
|
||||
}
|
||||
|
||||
private removeEventListeners(): void {
|
||||
if (this.sendButton && this.sendButtonEventHandler) {
|
||||
(this.sendButton as HTMLButtonElement).removeEventListener(
|
||||
'click',
|
||||
this.sendButtonEventHandler
|
||||
);
|
||||
if (this.sendButton && this.sendButtonClickWrapper) {
|
||||
this.sendButton.removeEventListener('click', this.sendButtonClickWrapper);
|
||||
}
|
||||
if (this.inputEl && this.inputKeyDownEventHandler) {
|
||||
(this.inputEl as HTMLTextAreaElement).removeEventListener(
|
||||
'keydown',
|
||||
this.inputKeyDownEventHandler
|
||||
);
|
||||
if (this.inputEl && this.inputKeyDownWrapper) {
|
||||
this.inputEl.removeEventListener('keydown', this.inputKeyDownWrapper);
|
||||
}
|
||||
if (this.newChatButton && this.newChatButtonEventHandler) {
|
||||
(this.newChatButton as HTMLButtonElement).removeEventListener(
|
||||
'click',
|
||||
this.newChatButtonEventHandler
|
||||
);
|
||||
if (this.newChatButton && this.newChatButtonClickWrapper) {
|
||||
this.newChatButton.removeEventListener('click', this.newChatButtonClickWrapper);
|
||||
}
|
||||
this.sendButtonEventHandler = null;
|
||||
this.inputKeyDownEventHandler = null;
|
||||
this.newChatButtonEventHandler = null;
|
||||
this.sendButtonClickWrapper = null;
|
||||
this.inputKeyDownWrapper = null;
|
||||
this.newChatButtonClickWrapper = null;
|
||||
this.listenersAttached = false;
|
||||
}
|
||||
|
||||
@@ -283,7 +270,7 @@ export class ChatView extends ItemView {
|
||||
|
||||
private async handleUserInput(content: string) {
|
||||
if (!this.sendButton || !this.inputEl) return;
|
||||
(this.sendButton as HTMLButtonElement).disabled = true;
|
||||
this.sendButton.disabled = true;
|
||||
|
||||
try {
|
||||
// Guard against empty messages
|
||||
@@ -483,7 +470,7 @@ export class ChatView extends ItemView {
|
||||
this.cleanupStreamingResources();
|
||||
} finally {
|
||||
if (this.sendButton) {
|
||||
(this.sendButton as HTMLButtonElement).disabled = false;
|
||||
this.sendButton.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user