mirror of
https://github.com/github/awesome-copilot.git
synced 2026-10-12 23:04:14 +00:00
Optimize catalog filter membership checks (#4448)
* Optimize catalog filter membership checks Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * perf: cache catalog filter bucket lookups Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * perf: reduce repeated category scans in search Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot App
parent
804187ef0d
commit
0052323536
@@ -152,13 +152,14 @@ export function AgentsCatalog({
|
||||
|
||||
const sortedAgents = useMemo(() => {
|
||||
const copy = agents.filter((agent) => {
|
||||
const models = modelsOf(agent);
|
||||
const modelSet = new Set(modelsOf(agent));
|
||||
const toolSet = new Set(agent.tools ?? []);
|
||||
const modelOk =
|
||||
filters.model.length === 0 ||
|
||||
filters.model.some((model) => models.includes(model));
|
||||
filters.model.some((model) => modelSet.has(model));
|
||||
const toolsOk =
|
||||
filters.tools.length === 0 ||
|
||||
filters.tools.every((tool) => (agent.tools ?? []).includes(tool));
|
||||
filters.tools.every((tool) => toolSet.has(tool));
|
||||
const updatedOk =
|
||||
filters.updated.length === 0 ||
|
||||
filters.updated.includes(updatedBucketOf(daysSince(agent.lastUpdated)));
|
||||
|
||||
@@ -152,11 +152,12 @@ export function PluginsCatalog({
|
||||
|
||||
const sortedPlugins = useMemo(() => {
|
||||
const copy = plugins.filter((plugin) => {
|
||||
const tagSet = new Set(plugin.tags ?? []);
|
||||
const sourceOk =
|
||||
filters.source.length === 0 || filters.source.includes(sourceOf(plugin));
|
||||
const categoryOk =
|
||||
filters.category.length === 0 ||
|
||||
filters.category.some((tag) => (plugin.tags ?? []).includes(tag));
|
||||
filters.category.some((tag) => tagSet.has(tag));
|
||||
const size = sizeLabelOf(plugin);
|
||||
const sizeOk =
|
||||
filters.size.length === 0 ||
|
||||
|
||||
@@ -140,6 +140,7 @@ export function TopNavSearch({
|
||||
const groups = useMemo<ResultGroup[]>(() => {
|
||||
const query = trimmed.toLowerCase();
|
||||
if (query.length === 0) return [];
|
||||
|
||||
const staticMatches = normalizedIndex
|
||||
.filter(
|
||||
({ title, description, category }) =>
|
||||
@@ -148,25 +149,33 @@ export function TopNavSearch({
|
||||
category.includes(query),
|
||||
)
|
||||
.map(({ item }) => item);
|
||||
|
||||
// Static hits win on ties: they carry curated titles and descriptions.
|
||||
const seen = new Set(staticMatches.map((item) => hrefKey(item.href)));
|
||||
const matches = [...staticMatches];
|
||||
const matches: SearchItem[] = [...staticMatches];
|
||||
for (const hit of pagefindHits) {
|
||||
const key = hrefKey(hit.href);
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
matches.push(hit);
|
||||
}
|
||||
|
||||
const matchesByCategory = new Map<SearchCategory, SearchItem[]>();
|
||||
for (const item of matches) {
|
||||
const bucket = matchesByCategory.get(item.category) ?? [];
|
||||
bucket.push(item);
|
||||
matchesByCategory.set(item.category, bucket);
|
||||
}
|
||||
|
||||
let remaining = MAX_RESULTS;
|
||||
const grouped: ResultGroup[] = [];
|
||||
for (const category of CATEGORY_ORDER) {
|
||||
if (remaining <= 0) break;
|
||||
const items = matches
|
||||
.filter((item) => item.category === category)
|
||||
.slice(0, Math.min(MAX_PER_GROUP, remaining));
|
||||
if (items.length === 0) continue;
|
||||
remaining -= items.length;
|
||||
grouped.push({ category, items });
|
||||
const items = matchesByCategory.get(category) ?? [];
|
||||
const slice = items.slice(0, Math.min(MAX_PER_GROUP, remaining));
|
||||
if (slice.length === 0) continue;
|
||||
remaining -= slice.length;
|
||||
grouped.push({ category, items: slice });
|
||||
}
|
||||
return grouped;
|
||||
}, [trimmed, normalizedIndex, pagefindHits]);
|
||||
|
||||
@@ -15,15 +15,34 @@ export const updatedBuckets: { label: string; max: number }[] = [
|
||||
];
|
||||
|
||||
/** Whole days between `lastUpdated` and now. Unparsable dates sort as oldest. */
|
||||
const daysSinceCache = new Map<string, number>();
|
||||
|
||||
export function daysSince(lastUpdated: string | undefined): number {
|
||||
if (!lastUpdated) return Number.POSITIVE_INFINITY;
|
||||
|
||||
const dayStamp = Math.floor(Date.now() / 86_400_000);
|
||||
const cacheKey = `${lastUpdated}:${dayStamp}`;
|
||||
const cached = daysSinceCache.get(cacheKey);
|
||||
if (cached !== undefined) return cached;
|
||||
|
||||
const then = Date.parse(lastUpdated);
|
||||
if (Number.isNaN(then)) return Number.POSITIVE_INFINITY;
|
||||
return Math.max(0, Math.floor((Date.now() - then) / 86_400_000));
|
||||
const days = Number.isNaN(then)
|
||||
? Number.POSITIVE_INFINITY
|
||||
: Math.max(0, Math.floor((Date.now() - then) / 86_400_000));
|
||||
|
||||
daysSinceCache.set(cacheKey, days);
|
||||
return days;
|
||||
}
|
||||
|
||||
const updatedBucketCache = new Map<number, string>();
|
||||
|
||||
export function updatedBucketOf(days: number): string {
|
||||
return updatedBuckets.find((bucket) => days <= bucket.max)?.label ?? "Older";
|
||||
const cached = updatedBucketCache.get(days);
|
||||
if (cached) return cached;
|
||||
|
||||
const bucket = updatedBuckets.find((option) => days <= option.max)?.label ?? "Older";
|
||||
updatedBucketCache.set(days, bucket);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
export const fileBuckets: { label: string; min: number; max: number }[] = [
|
||||
@@ -33,11 +52,18 @@ export const fileBuckets: { label: string; min: number; max: number }[] = [
|
||||
{ label: "11+ files", min: 11, max: Number.POSITIVE_INFINITY },
|
||||
];
|
||||
|
||||
const fileBucketCache = new Map<number, string>();
|
||||
|
||||
export function fileBucketOf(files: number): string {
|
||||
return (
|
||||
fileBuckets.find((bucket) => files >= bucket.min && files <= bucket.max)
|
||||
?.label ?? "1–2 files"
|
||||
);
|
||||
const cached = fileBucketCache.get(files);
|
||||
if (cached) return cached;
|
||||
|
||||
const bucket =
|
||||
fileBuckets.find((option) => files >= option.min && files <= option.max)?.label ??
|
||||
"1–2 files";
|
||||
|
||||
fileBucketCache.set(files, bucket);
|
||||
return bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user