fix: repair broken filter controls in notification history modal
- Replace raw HTML inputs with Obsidian Setting components - Add DOM element references for targeted updates - Eliminate destructive re-render on filter changes - Update only list container and count on filter apply - Fix tool filter input not accepting text - Fix status dropdown not showing selection Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { App, Modal } from 'obsidian';
|
import { App, Modal, Setting } from 'obsidian';
|
||||||
import { NotificationHistoryEntry } from './notifications';
|
import { NotificationHistoryEntry } from './notifications';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,6 +10,10 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
private filterTool: string = '';
|
private filterTool: string = '';
|
||||||
private filterType: 'all' | 'success' | 'error' = 'all';
|
private filterType: 'all' | 'success' | 'error' = 'all';
|
||||||
|
|
||||||
|
// DOM element references for targeted updates
|
||||||
|
private listContainerEl: HTMLElement | null = null;
|
||||||
|
private countEl: HTMLElement | null = null;
|
||||||
|
|
||||||
constructor(app: App, history: NotificationHistoryEntry[]) {
|
constructor(app: App, history: NotificationHistoryEntry[]) {
|
||||||
super(app);
|
super(app);
|
||||||
this.history = history;
|
this.history = history;
|
||||||
@@ -24,11 +28,11 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
// Title
|
// Title
|
||||||
contentEl.createEl('h2', { text: 'MCP Notification History' });
|
contentEl.createEl('h2', { text: 'MCP Notification History' });
|
||||||
|
|
||||||
// Filters
|
// Filters (create once, never recreate)
|
||||||
this.createFilters(contentEl);
|
this.createFilters(contentEl);
|
||||||
|
|
||||||
// History list
|
// History list (will be updated via reference)
|
||||||
this.createHistoryList(contentEl);
|
this.createHistoryListContainer(contentEl);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
this.createActions(contentEl);
|
this.createActions(contentEl);
|
||||||
@@ -37,68 +41,77 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
onClose() {
|
onClose() {
|
||||||
const { contentEl } = this;
|
const { contentEl } = this;
|
||||||
contentEl.empty();
|
contentEl.empty();
|
||||||
|
this.listContainerEl = null;
|
||||||
|
this.countEl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create filter controls
|
* Create filter controls using Obsidian Setting components
|
||||||
*/
|
*/
|
||||||
private createFilters(containerEl: HTMLElement): void {
|
private createFilters(containerEl: HTMLElement): void {
|
||||||
const filterContainer = containerEl.createDiv({ cls: 'mcp-history-filters' });
|
const filterContainer = containerEl.createDiv({ cls: 'mcp-history-filters' });
|
||||||
filterContainer.style.marginBottom = '16px';
|
filterContainer.style.marginBottom = '16px';
|
||||||
filterContainer.style.display = 'flex';
|
|
||||||
filterContainer.style.gap = '12px';
|
|
||||||
filterContainer.style.flexWrap = 'wrap';
|
|
||||||
|
|
||||||
// Tool name filter
|
// Tool name filter using Setting component
|
||||||
const toolFilterContainer = filterContainer.createDiv();
|
new Setting(filterContainer)
|
||||||
toolFilterContainer.createEl('label', { text: 'Tool: ' });
|
.setName('Tool filter')
|
||||||
const toolInput = toolFilterContainer.createEl('input', {
|
.setDesc('Filter by tool name')
|
||||||
type: 'text',
|
.addText(text => text
|
||||||
placeholder: 'Filter by tool name...'
|
.setPlaceholder('Enter tool name...')
|
||||||
});
|
.setValue(this.filterTool)
|
||||||
toolInput.style.marginLeft = '4px';
|
.onChange((value) => {
|
||||||
toolInput.style.padding = '4px 8px';
|
this.filterTool = value.toLowerCase();
|
||||||
toolInput.addEventListener('input', (e) => {
|
this.applyFilters();
|
||||||
this.filterTool = (e.target as HTMLInputElement).value.toLowerCase();
|
}));
|
||||||
this.applyFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Type filter
|
// Type filter using Setting component
|
||||||
const typeFilterContainer = filterContainer.createDiv();
|
new Setting(filterContainer)
|
||||||
typeFilterContainer.createEl('label', { text: 'Type: ' });
|
.setName('Status filter')
|
||||||
const typeSelect = typeFilterContainer.createEl('select');
|
.setDesc('Filter by success or error')
|
||||||
typeSelect.style.marginLeft = '4px';
|
.addDropdown(dropdown => dropdown
|
||||||
typeSelect.style.padding = '4px 8px';
|
.addOption('all', 'All')
|
||||||
|
.addOption('success', 'Success')
|
||||||
const allOption = typeSelect.createEl('option', { text: 'All', value: 'all' });
|
.addOption('error', 'Error')
|
||||||
const successOption = typeSelect.createEl('option', { text: 'Success', value: 'success' });
|
.setValue(this.filterType)
|
||||||
const errorOption = typeSelect.createEl('option', { text: 'Error', value: 'error' });
|
.onChange((value) => {
|
||||||
|
this.filterType = value as 'all' | 'success' | 'error';
|
||||||
typeSelect.addEventListener('change', (e) => {
|
this.applyFilters();
|
||||||
this.filterType = (e.target as HTMLSelectElement).value as 'all' | 'success' | 'error';
|
}));
|
||||||
this.applyFilters();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Results count
|
// Results count
|
||||||
const countEl = filterContainer.createDiv({ cls: 'mcp-history-count' });
|
this.countEl = filterContainer.createDiv({ cls: 'mcp-history-count' });
|
||||||
countEl.style.marginLeft = 'auto';
|
this.countEl.style.marginTop = '8px';
|
||||||
countEl.style.alignSelf = 'center';
|
this.countEl.style.fontSize = '0.9em';
|
||||||
countEl.textContent = `${this.filteredHistory.length} entries`;
|
this.countEl.style.color = 'var(--text-muted)';
|
||||||
|
this.updateResultsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create history list
|
* Create history list container (called once)
|
||||||
*/
|
*/
|
||||||
private createHistoryList(containerEl: HTMLElement): void {
|
private createHistoryListContainer(containerEl: HTMLElement): void {
|
||||||
const listContainer = containerEl.createDiv({ cls: 'mcp-history-list' });
|
this.listContainerEl = containerEl.createDiv({ cls: 'mcp-history-list' });
|
||||||
listContainer.style.maxHeight = '400px';
|
this.listContainerEl.style.maxHeight = '400px';
|
||||||
listContainer.style.overflowY = 'auto';
|
this.listContainerEl.style.overflowY = 'auto';
|
||||||
listContainer.style.marginBottom = '16px';
|
this.listContainerEl.style.marginBottom = '16px';
|
||||||
listContainer.style.border = '1px solid var(--background-modifier-border)';
|
this.listContainerEl.style.border = '1px solid var(--background-modifier-border)';
|
||||||
listContainer.style.borderRadius = '4px';
|
this.listContainerEl.style.borderRadius = '4px';
|
||||||
|
|
||||||
|
// Initial render
|
||||||
|
this.updateHistoryList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update history list contents (called on filter changes)
|
||||||
|
*/
|
||||||
|
private updateHistoryList(): void {
|
||||||
|
if (!this.listContainerEl) return;
|
||||||
|
|
||||||
|
// Clear existing content
|
||||||
|
this.listContainerEl.empty();
|
||||||
|
|
||||||
if (this.filteredHistory.length === 0) {
|
if (this.filteredHistory.length === 0) {
|
||||||
const emptyEl = listContainer.createDiv({ cls: 'mcp-history-empty' });
|
const emptyEl = this.listContainerEl.createDiv({ cls: 'mcp-history-empty' });
|
||||||
emptyEl.style.padding = '24px';
|
emptyEl.style.padding = '24px';
|
||||||
emptyEl.style.textAlign = 'center';
|
emptyEl.style.textAlign = 'center';
|
||||||
emptyEl.style.color = 'var(--text-muted)';
|
emptyEl.style.color = 'var(--text-muted)';
|
||||||
@@ -107,10 +120,10 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.filteredHistory.forEach((entry, index) => {
|
this.filteredHistory.forEach((entry, index) => {
|
||||||
const entryEl = listContainer.createDiv({ cls: 'mcp-history-entry' });
|
const entryEl = this.listContainerEl!.createDiv({ cls: 'mcp-history-entry' });
|
||||||
entryEl.style.padding = '12px';
|
entryEl.style.padding = '12px';
|
||||||
entryEl.style.borderBottom = index < this.filteredHistory.length - 1
|
entryEl.style.borderBottom = index < this.filteredHistory.length - 1
|
||||||
? '1px solid var(--background-modifier-border)'
|
? '1px solid var(--background-modifier-border)'
|
||||||
: 'none';
|
: 'none';
|
||||||
|
|
||||||
// Header row
|
// Header row
|
||||||
@@ -160,6 +173,14 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update results count display
|
||||||
|
*/
|
||||||
|
private updateResultsCount(): void {
|
||||||
|
if (!this.countEl) return;
|
||||||
|
this.countEl.textContent = `${this.filteredHistory.length} of ${this.history.length} entries`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create action buttons
|
* Create action buttons
|
||||||
*/
|
*/
|
||||||
@@ -209,7 +230,8 @@ export class NotificationHistoryModal extends Modal {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Re-render
|
// Update only the affected UI elements
|
||||||
this.onOpen();
|
this.updateHistoryList();
|
||||||
|
this.updateResultsCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user