mirror of
https://github.com/railly/agentfiles.git
synced 2026-07-22 06:12:56 +00:00
feat: toggleable deep search with configurable scope
Add a deep search toggle button in the search bar that controls whether search includes description and/or file content beyond just the name. - Default off (matching upstream name-only search behavior) - Settings: "Deep search by default" toggle + "Deep search scope" dropdown (description only / file content only / both) - Toggle button sits alongside existing filter and sort buttons Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4d433a23c6
commit
28daafa3df
8 changed files with 2789 additions and 6 deletions
6
main.js
6
main.js
File diff suppressed because one or more lines are too long
2667
package-lock.json
generated
Normal file
2667
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -35,6 +35,8 @@ export default class AgentfilesPlugin extends Plugin {
|
|||
this.addSettingTab(new AgentfilesSettingTab(this.app, this));
|
||||
|
||||
this.refreshStore();
|
||||
this.store.setDeepSearch(this.settings.deepSearchDefault ?? false);
|
||||
this.store.setDeepSearchScope(this.settings.deepSearchScope ?? "both");
|
||||
this.startWatcher();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,37 @@ export class AgentfilesSettingTab extends PluginSettingTab {
|
|||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Deep search by default")
|
||||
.setDesc(
|
||||
"Enable deep search when the view opens (can always be toggled in the search bar)"
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.deepSearchDefault ?? false)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.deepSearchDefault = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Deep search scope")
|
||||
.setDesc("What to include when deep search is enabled")
|
||||
.addDropdown((drop) =>
|
||||
drop
|
||||
.addOptions({
|
||||
both: "Description and file content",
|
||||
description: "Description only",
|
||||
content: "File content only",
|
||||
})
|
||||
.setValue(this.plugin.settings.deepSearchScope ?? "both")
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.deepSearchScope = value as "description" | "content" | "both";
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl).setName("Marketplace").setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
|
|
|
|||
26
src/store.ts
26
src/store.ts
|
|
@ -1,5 +1,5 @@
|
|||
import { Events } from "obsidian";
|
||||
import type { SkillItem, SidebarFilter, ChopsSettings } from "./types";
|
||||
import type { SkillItem, SidebarFilter, DeepSearchScope, ChopsSettings } from "./types";
|
||||
import { scanAll, getProjectName } from "./scanner";
|
||||
import { getSkillkitStatsWithDaily, getSkillConflicts, getSkillWarnings, isSkillkitAvailable } from "./skillkit";
|
||||
|
||||
|
|
@ -7,6 +7,8 @@ export class SkillStore extends Events {
|
|||
private items: Map<string, SkillItem> = new Map();
|
||||
private _filter: SidebarFilter = { kind: "all" };
|
||||
private _searchQuery = "";
|
||||
private _deepSearch = false;
|
||||
private _deepSearchScope: DeepSearchScope = "both";
|
||||
private _projectsHomeDir = "";
|
||||
|
||||
get filter(): SidebarFilter {
|
||||
|
|
@ -17,6 +19,10 @@ export class SkillStore extends Events {
|
|||
return this._searchQuery;
|
||||
}
|
||||
|
||||
get deepSearch(): boolean {
|
||||
return this._deepSearch;
|
||||
}
|
||||
|
||||
get allItems(): SkillItem[] {
|
||||
return Array.from(this.items.values());
|
||||
}
|
||||
|
|
@ -50,9 +56,13 @@ export class SkillStore extends Events {
|
|||
|
||||
if (this._searchQuery) {
|
||||
const q = this._searchQuery.toLowerCase();
|
||||
const searchDesc = this._deepSearch && (this._deepSearchScope === "description" || this._deepSearchScope === "both");
|
||||
const searchContent = this._deepSearch && (this._deepSearchScope === "content" || this._deepSearchScope === "both");
|
||||
result = result.filter(
|
||||
(i) =>
|
||||
i.name.toLowerCase().includes(q)
|
||||
i.name.toLowerCase().includes(q) ||
|
||||
(searchDesc && i.description.toLowerCase().includes(q)) ||
|
||||
(searchContent && i.content.toLowerCase().includes(q))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +136,18 @@ export class SkillStore extends Events {
|
|||
this.trigger("updated");
|
||||
}
|
||||
|
||||
setDeepSearch(enabled: boolean): void {
|
||||
if (this._deepSearch === enabled) return;
|
||||
this._deepSearch = enabled;
|
||||
this.trigger("updated");
|
||||
}
|
||||
|
||||
setDeepSearchScope(scope: DeepSearchScope): void {
|
||||
if (this._deepSearchScope === scope) return;
|
||||
this._deepSearchScope = scope;
|
||||
this.trigger("updated");
|
||||
}
|
||||
|
||||
toggleFavorite(id: string, settings: ChopsSettings): void {
|
||||
const item = this.items.get(id);
|
||||
if (!item) return;
|
||||
|
|
|
|||
|
|
@ -68,10 +68,14 @@ export type SidebarFilter =
|
|||
|
||||
export type NamingMode = "auto" | "filename";
|
||||
|
||||
export type DeepSearchScope = "description" | "content" | "both";
|
||||
|
||||
export interface ChopsSettings {
|
||||
tools: Record<string, { enabled: boolean; customPaths: string[] }>;
|
||||
watchEnabled: boolean;
|
||||
watchDebounceMs: number;
|
||||
deepSearchDefault: boolean;
|
||||
deepSearchScope: DeepSearchScope;
|
||||
favorites: string[];
|
||||
collections: Record<string, string[]>;
|
||||
customScanPaths: string[];
|
||||
|
|
@ -85,6 +89,8 @@ export const DEFAULT_SETTINGS: ChopsSettings = {
|
|||
tools: {},
|
||||
watchEnabled: true,
|
||||
watchDebounceMs: 500,
|
||||
deepSearchDefault: false,
|
||||
deepSearchScope: "both",
|
||||
favorites: [],
|
||||
collections: {},
|
||||
customScanPaths: [],
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export class ListPanel {
|
|||
private onSelect: (item: SkillItem) => void;
|
||||
private selectedId: string | null = null;
|
||||
private inputEl: HTMLInputElement | null = null;
|
||||
private deepToggleEl: HTMLElement | null = null;
|
||||
private listEl: HTMLElement | null = null;
|
||||
private typeFilter: string | null = null;
|
||||
private sortBy: "name" | "usage" = "name";
|
||||
|
|
@ -44,6 +45,14 @@ export class ListPanel {
|
|||
this.store.setSearch(this.inputEl!.value);
|
||||
});
|
||||
|
||||
this.deepToggleEl = searchContainer.createDiv("as-deep-toggle");
|
||||
setIcon(this.deepToggleEl, "search-code");
|
||||
this.deepToggleEl.setAttribute("aria-label", "Toggle deep search");
|
||||
this.deepToggleEl.addEventListener("click", () => {
|
||||
this.store.setDeepSearch(!this.store.deepSearch);
|
||||
this.updateDeepToggle();
|
||||
});
|
||||
|
||||
const filterBtn = searchContainer.createDiv("as-filter-btn");
|
||||
setIcon(filterBtn, "filter");
|
||||
filterBtn.setAttribute("aria-label", "Filter by status");
|
||||
|
|
@ -66,9 +75,21 @@ export class ListPanel {
|
|||
}
|
||||
|
||||
this.inputEl.value = this.store.searchQuery;
|
||||
this.updateDeepToggle();
|
||||
this.renderList();
|
||||
}
|
||||
|
||||
private updateDeepToggle(): void {
|
||||
if (!this.deepToggleEl) return;
|
||||
this.deepToggleEl.toggleClass("is-active", this.store.deepSearch);
|
||||
this.deepToggleEl.setAttribute(
|
||||
"aria-label",
|
||||
this.store.deepSearch
|
||||
? "Deep search enabled — click to search names only"
|
||||
: "Deep search disabled — click to search file content"
|
||||
);
|
||||
}
|
||||
|
||||
private toggleDropdown(anchor: HTMLElement): void {
|
||||
if (this.dropdownEl) {
|
||||
this.dropdownEl.remove();
|
||||
|
|
|
|||
36
styles.css
36
styles.css
|
|
@ -132,12 +132,16 @@
|
|||
|
||||
/* Search */
|
||||
.as-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.as-search-input {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
|
|
@ -152,6 +156,36 @@
|
|||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--interactive-accent) 25%, transparent);
|
||||
}
|
||||
|
||||
.as-deep-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex-shrink: 0;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-s);
|
||||
background: var(--background-primary);
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.as-deep-toggle:hover {
|
||||
color: var(--text-muted);
|
||||
border-color: var(--text-faint);
|
||||
}
|
||||
|
||||
.as-deep-toggle.is-active {
|
||||
background: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.as-deep-toggle svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
/* List */
|
||||
.as-list-items {
|
||||
padding: 4px;
|
||||
|
|
|
|||
Loading…
Reference in a new issue