/** * The five tools, declared once. * * Each Space linked to the others from its own footer, which meant the * same five URLs written out in five files — twenty-five places for a * rename to break, and a Space rename is invisible from the linking * side: the link keeps returning a page, and the page is a 404. * * So the registry lives here and is copied into each Space at deploy * time, next to `progress.js`. One file to change when a tool moves. * * The order is the order somebody actually needs them: find a model, * see how much context it can serve, learn how to run it, then check * what the pack really contains before spending the download. */ export const TOOLS = { search: { space: 'model-search-that-answers', title: 'Find a model that fits', asks: 'which models fit my card at all', // What the visitor has just learned, and why the next tool follows. leadsTo: 'context', takes: 'query', }, context: { space: 'what-context-fits', title: 'What context will actually fit?', asks: 'how long a conversation this one can serve', leadsTo: 'parser', takes: 'repo', }, parser: { space: 'reasoning-parser-advisor', title: 'Does this model need a reasoning parser?', asks: 'how to serve it without silently empty answers', leadsTo: 'precision', takes: 'repo', }, precision: { space: 'pack-precision-map', title: 'How much of this pack is actually 4-bit?', asks: 'what a quantized pack really compressed, and what it kept', leadsTo: 'integrity', takes: 'repo', }, integrity: { space: 'pack-integrity-check', title: 'Does this pack keep what it claims?', asks: 'whether anything was dropped or left undeclared', leadsTo: null, takes: 'repo', }, }; export const ORDER = ['search', 'context', 'parser', 'precision', 'integrity']; const BASE = 'https://huggingface.co/spaces/aleada'; const HUB = `${BASE}/start-here`; /** * A link to one tool, carrying the model with it. * * A funnel where each step starts empty is five separate tools with * links between them. `?repo=` is read on load by every page that takes * one, so clicking through keeps the model you were looking at. */ export function url(key, repo = null) { const t = TOOLS[key]; if (!t) return BASE; const q = repo && t.takes === 'repo' ? `?repo=${encodeURIComponent(repo)}` : repo && t.takes === 'query' ? `?q=${encodeURIComponent(repo)}` : ''; return `${BASE}/${t.space}${q}`; } /** The model this page was opened with, if a link carried one over. */ export function repoFromUrl() { const p = new URLSearchParams(window.location.search); return (p.get('repo') || p.get('q') || '').trim() || null; } const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); /** * The next questions, rendered under a result. * * The whole chain is rendered, not just the parts that come after: the * current step is marked so a visitor who arrived from a link can see * where they are, and the steps behind stay reachable because the order * is a suggestion and not a gate. * * Offered only once there is an answer on screen — a next step shown * before the current one is finished is clutter, and one that cannot * carry the model over is a link to an empty form. */ export function nextSteps(fromKey, repo = null) { const here = ORDER.indexOf(fromKey); const items = ORDER.map((k, i) => { const t = TOOLS[k]; if (i === here) { // The step you are on, shown rather than hidden. A list of "other // tools" tells you what exists; a chain with your place marked // tells you how far along you are, which is the thing somebody // arriving from a link actually wants to know. return `