export type SavedProject = { project_id: string; title: string; updated: string; versions: number }; /** Groups builds (newest first) into one project per session; the title is the first prompt. */ export function groupProjects(rows: { session_id: string; prompt: string; created_at: string }[]): SavedProject[] { const map = new Map(); for (const r of rows) { const p = map.get(r.session_id); if (p) { p.versions++; p.title = r.prompt.slice(0, 80); } else map.set(r.session_id, { project_id: r.session_id, title: r.prompt.slice(0, 80), updated: r.created_at, versions: 1 }); } return [...map.values()]; } /** Picks a project by exact id, "latest"/"last", list number ("2"), or the most matching title words. */ export function matchProject(projects: SavedProject[], query: string): SavedProject | null { const q = query.trim().toLowerCase(); if (!projects.length || !q) return null; const byId = projects.find((p) => p.project_id === q); if (byId) return byId; if (/^(latest|last|newest|recent|most recent)( project| one)?$/.test(q)) return projects[0]!; const n = Number(q.replace(/^(number|#|project)\s*/, "")); if (Number.isInteger(n) && n >= 1 && n <= projects.length) return projects[n - 1]!; const words = q.split(/\W+/).filter((w) => w.length > 2 && !["the", "project", "website", "site", "app", "one", "my"].includes(w)); let best: SavedProject | null = null, score = 0; for (const p of projects) { const t = p.title.toLowerCase(); const s = words.filter((w) => t.includes(w)).length; if (s > score) { best = p; score = s; } } return best; }