返回 CodeWhale
catalogue.ts
根目录 / web / lib / ratatui / catalogue.ts
1 import catalogue from "@/public/ratatui/catalogue.json";
2 import { discoveryText, displayApi, normalizeSearch } from "./learning";
3
4 export interface CatalogueEntry {
5 name: string;
6 title: string;
7 family: string;
8 width: number;
9 height: number;
10 description: string;
11 api: string[];
12 source: { file: string; function: string; line: number; url: string };
13 previewPath: string;
14 }
15
16 export interface Catalogue {
17 schemaVersion: number;
18 source: { revision: string; digest: string; repository: string; nativeRevision: string };
19 profiles: { id: string; label: string }[];
20 sizes: { id: string; label: string }[];
21 families: { id: string; title: string; description: string; count: number }[];
22 entries: CatalogueEntry[];
23 }
24
25 export interface EntryPreview {
26 name: string;
27 width: number;
28 height: number;
29 previews: Record<string, Record<string, string>>;
30 fixture: { code: string; imports: string; helpers?: { name: string; code: string; line: number }[]; source: unknown };
31 }
32
33 /** Bundled at build time: no filesystem or remote repository dependency in the Worker. */
34 export function readCatalogue(): Catalogue {
35 return catalogue;
36 }
37
38 export function searchEntries(entries: readonly CatalogueEntry[], query: string, family = "all") {
39 const terms = normalizeSearch(query).split(/\s+/).filter(Boolean);
40 return entries.filter((entry) => {
41 if (family !== "all" && entry.family !== family) return false;
42 const text = normalizeSearch([entry.name, entry.title, ...displayApi(entry), discoveryText(entry)].join(" "));
43 return terms.every((term) => text.includes(term));
44 });
45 }
46
47 export function previewAssetPath(entry: CatalogueEntry) {
48 if (!/^entries\/[a-z0-9_-]+\.json$/.test(entry.previewPath)) throw new Error("Invalid catalogue path");
49 return `/ratatui/${entry.previewPath}`;
50 }
51
52 export function motionId(entry: CatalogueEntry, profile: string) {
53 if (entry.name === "showcase-life" || ["whales", "whale-actions"].includes(entry.family)) return "whale";
54 if (entry.family === "habitat") return "habitat";
55 if (entry.family === "motion") return profile === "light-truecolor" ? "motion-light" : "motion";
56 if (entry.family === "studio") return profile === "light-truecolor" ? "studio-light" : "studio";
57 return null;
58 }
59
59 lines TYPESCRIPT