Spaces:
Running
Running
File size: 3,004 Bytes
a32aee9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
/** Merge Tailwind class names with conflict resolution. */
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}
/** Stable colour for a research-topic badge (mirrors app/topic_classifier.py). */
const TOPIC_COLOURS: Record<string, string> = {
"Machine Learning": "#6366f1",
"Deep Learning": "#8b5cf6",
"NLP / Large Language Models": "#06b6d4",
"Computer Vision": "#10b981",
"RAG / Information Retrieval": "#f59e0b",
"Reinforcement Learning": "#ef4444",
"Bioinformatics / Computational Biology": "#84cc16",
"Robotics / Control Systems": "#f97316",
"Data Science / Statistics": "#3b82f6",
"Security / Privacy": "#ec4899",
"Human-Computer Interaction": "#14b8a6",
Other: "#6b7280",
};
export function topicColour(topic: string): string {
return TOPIC_COLOURS[topic] ?? "#6b7280";
}
/** Colour ramp for similarity/relevance scores [0,1]. */
export function scoreColour(score: number): string {
if (score >= 0.5) return "#10b981";
if (score >= 0.3) return "#f59e0b";
return "#ef4444";
}
export function truncate(text: string, max: number): string {
return text.length > max ? `${text.slice(0, max)}…` : text;
}
/**
* Strip a leading emoji / symbol run from backend-provided strings (the model
* catalog and provider hints ship with emoji prefixes). Keeps the UI emoji-free
* per the design skills without touching the backend.
*/
export function stripLeadingSymbols(text: string): string {
return text.replace(/^[^\p{L}\p{N}]+/u, "").trim();
}
/**
* Reference source labels. The backend uses four distinct values:
* "upload" — a PDF the user uploaded (in the KB)
* "openalex" — an OpenAlex abstract ingested into the KB
* "openalex-live" — a LIVE OpenAlex fallback (NOT in the KB)
* "web" — a DuckDuckGo web fallback (NOT in the KB)
* See app/fallback.py and app/rag.py.build_context.
*/
export interface SourceMeta {
label: string;
/** True when the reference actually comes from the user's knowledge base. */
fromKb: boolean;
className: string;
}
export function sourceMeta(source: string): SourceMeta {
switch (source) {
case "upload":
return { label: "Uploaded", fromKb: true, className: "bg-emerald-500/15 text-emerald-300" };
case "openalex":
return { label: "OpenAlex", fromKb: true, className: "bg-orange-500/15 text-orange-300" };
case "openalex-live":
return { label: "OpenAlex · live", fromKb: false, className: "bg-sky-500/15 text-sky-300" };
case "web":
return { label: "Web", fromKb: false, className: "bg-amber-500/15 text-amber-300" };
default:
return { label: source || "Source", fromKb: false, className: "bg-slate-600/30 text-slate-300" };
}
}
/** Format a value that may be a number or the sentinel "?" for page numbers. */
export function formatScore(score: number): string {
return score.toFixed(3);
}
|