import { ref, computed, readonly } from "vue"; export interface Template { name: string; type: string; description: string; content?: string; downloadUrl?: string; rawUrl?: string; size?: number; } interface Pagination { page: number; limit: number; total: number; totalPages: number; hasMore: boolean; } // Global state const templates = ref([]); const filter = ref("all"); const search = ref(""); const isLoading = ref(false); const pagination = ref({ page: 1, limit: 50, total: 0, totalPages: 0, hasMore: false, }); const counts = ref>({ all: 0, agents: 0, commands: 0, mcps: 0, skills: 0, settings: 0, hooks: 0, }); // Fetch counts on init async function fetchCounts() { try { // Fetch all to get counts (small payload since we just need counts) const res = await fetch("/api/templates?limit=1000"); if (res.ok) { const data = await res.json(); const all = data.templates as Template[]; counts.value = { all: all.length, agents: all.filter((t) => t.type === "agent").length, commands: all.filter((t) => t.type === "command").length, mcps: all.filter((t) => t.type === "mcp").length, skills: all.filter((t) => t.type === "skill").length, settings: all.filter((t) => t.type === "setting").length, hooks: all.filter((t) => t.type === "hook").length, }; } } catch { // Silent fail } } async function fetchTemplates(resetPage = true) { isLoading.value = true; try { const page = resetPage ? 1 : pagination.value.page; const params = new URLSearchParams({ filter: filter.value, page: String(page), limit: String(pagination.value.limit), }); if (search.value) { params.set("search", search.value); } const res = await fetch(`/api/templates?${params}`); if (res.ok) { const data = await res.json(); if (resetPage) { templates.value = data.templates; } else { // Append for infinite scroll templates.value = [...templates.value, ...data.templates]; } pagination.value = data.pagination; } } catch (error) { console.error("Failed to fetch templates:", error); } finally { isLoading.value = false; } } function setFilter(newFilter: string) { if (filter.value !== newFilter) { filter.value = newFilter; fetchTemplates(true); } } function setSearch(newSearch: string) { search.value = newSearch; fetchTemplates(true); } function loadMore() { if (pagination.value.hasMore && !isLoading.value) { pagination.value.page++; fetchTemplates(false); } } // Initialize with SSR data function initWithData( initialTemplates: Template[], initialCounts: Record, initialFilter = "all", ) { templates.value = initialTemplates; counts.value = initialCounts; filter.value = initialFilter; } export function useTemplates() { return { templates: readonly(templates), filter: readonly(filter), search: readonly(search), isLoading: readonly(isLoading), pagination: readonly(pagination), counts: readonly(counts), setFilter, setSearch, loadMore, fetchTemplates, fetchCounts, initWithData, }; }