feat: improve search modal UI

This commit is contained in:
ashwin271 2026-01-14 03:18:31 +00:00
parent 8425ba055e
commit b709e3ca9f
2 changed files with 79 additions and 8 deletions

47
main.ts
View file

@ -618,6 +618,7 @@ export default class VectorSearchPlugin extends Plugin {
class SearchModal extends Modal {
private plugin: VectorSearchPlugin;
private searchInput: HTMLInputElement;
private statusDiv: HTMLDivElement;
private resultsDiv: HTMLDivElement;
constructor(app: App, plugin: VectorSearchPlugin) {
@ -635,6 +636,12 @@ class SearchModal extends Modal {
type: 'text',
placeholder: 'Type to search similar notes...'
});
const actions = searchContainer.createDiv('search-actions');
const searchButton = actions.createEl('button', { text: 'Search' });
const selectionButton = actions.createEl('button', { text: 'Use selection' });
this.statusDiv = contentEl.createDiv('search-status');
// Create results container
this.resultsDiv = contentEl.createDiv('search-results');
@ -644,31 +651,61 @@ class SearchModal extends Modal {
const query = this.searchInput.value;
if (query.length < 3) {
this.resultsDiv.empty();
this.statusDiv.empty();
return;
}
await this.performSearch(query);
}, this.plugin.settings.debounceTime, true);
this.searchInput.addEventListener('input', debouncedSearch);
searchButton.addEventListener('click', async () => {
await this.performSearch(this.searchInput.value);
});
selectionButton.addEventListener('click', async () => {
const selection = this.getActiveSelection();
if (selection.length < 3) {
new Notice('Select at least 3 characters to search.');
return;
}
this.searchInput.value = selection;
await this.performSearch(selection);
});
// Focus input
this.searchInput.focus();
}
private getActiveSelection(): string {
const editor = this.app.workspace.activeEditor?.editor;
return editor?.getSelection().trim() ?? '';
}
async performSearch(query: string) {
if (query.length < 3) {
this.resultsDiv.setText('Type at least 3 characters to search.');
this.statusDiv.empty();
return;
}
this.statusDiv.setText('Searching...');
if (this.plugin.vectorStore.size === 0) {
this.resultsDiv.setText('Vector index is empty. Please rebuild the index first.');
this.statusDiv.empty();
return;
}
const isReady = await this.plugin.ensureRequirements(true);
if (!isReady) {
this.resultsDiv.setText('Ollama is unavailable. Check the plugin settings and try again.');
this.statusDiv.empty();
return;
}
const queryEmbedding = await this.plugin.getEmbedding(query);
if (queryEmbedding.length === 0) {
this.resultsDiv.setText('Failed to generate an embedding for the query.');
this.statusDiv.empty();
return;
}
const results: Array<{vectorData: VectorData, similarity: number}> = [];
for (const vectorData of this.plugin.vectorStore.values()) {
@ -680,6 +717,7 @@ class SearchModal extends Modal {
results.sort((a, b) => b.similarity - a.similarity);
this.displayResults(results.slice(0, this.plugin.settings.maxResults));
this.statusDiv.empty();
}
displayResults(results: Array<{vectorData: VectorData, similarity: number}>) {
@ -693,9 +731,10 @@ class SearchModal extends Modal {
const list = this.resultsDiv.createEl('ul');
for (const result of results) {
const item = list.createEl('li');
const link = item.createEl('a', {
text: `${result.vectorData.title} (${(result.similarity * 100).toFixed(2)}%)`,
href: '#'
const link = item.createEl('a', { text: result.vectorData.title, href: '#' });
item.createEl('span', {
text: `${(result.similarity * 100).toFixed(2)}%`,
cls: 'similarity-score'
});
// Add line numbers info

View file

@ -38,6 +38,30 @@
box-shadow: 0 0 0 2px var(--interactive-accent-hover);
}
.search-actions {
display: flex;
gap: 8px;
margin-top: 8px;
}
.search-actions button {
padding: 6px 10px;
border-radius: 6px;
border: 1px solid var(--background-modifier-border);
background-color: var(--background-secondary);
color: var(--text-normal);
cursor: pointer;
}
.search-actions button:hover {
background-color: var(--background-modifier-hover);
}
.search-status {
padding: 6px 12px;
color: var(--text-muted);
}
.search-results {
max-height: 60vh;
overflow-y: auto;
@ -56,6 +80,9 @@
border-radius: 6px;
background-color: var(--background-secondary);
transition: all 0.2s ease;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.search-results li:hover {
@ -66,9 +93,7 @@
.search-results a {
text-decoration: none;
color: var(--text-normal);
display: flex;
justify-content: space-between;
align-items: center;
flex: 1 1 auto;
}
.search-results .similarity-score {
@ -79,6 +104,13 @@
background-color: var(--background-modifier-border);
}
.search-result-lines {
width: 100%;
margin-top: 6px;
color: var(--text-muted);
font-size: 0.85em;
}
/* Progress Bar Styles */
.vector-search-progress {
width: 100%;
@ -93,4 +125,4 @@
height: 100%;
background-color: var(--interactive-accent);
transition: width 0.3s ease;
}
}