Spaces:
Running
Running
File size: 6,099 Bytes
173ff89 | 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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | /**
* 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 `<li class="ns-here"><span class="ns-mark">✓</span>
<span class="ns-title">${esc(t.title)}</span>
<span class="ns-why">you are here</span></li>`;
}
const ahead = i > here;
return `<li class="${ahead ? 'ns-next' : 'ns-back'}">
<span class="ns-mark">${ahead ? '→' : '·'}</span>
<a href="${url(k, repo)}" target="_blank" rel="noopener">${esc(t.title)}</a>
<span class="ns-why">${esc(t.asks)}</span></li>`;
}).join('');
return `<nav class="nextsteps" aria-label="The rest of the chain">
<h4>Your next question${repo ? ` for <code>${esc(repo)}</code>` : ''}</h4>
<ul>${items}</ul>
<a class="ns-hub" href="${HUB}${repo ? `?repo=${encodeURIComponent(repo)}` : ''}"
target="_blank" rel="noopener">All five, and why they are in this order →</a>
</nav>`;
}
/** Styles for the block above, injected once so each Space need not carry them. */
export function ensureNextStepStyles() {
if (document.getElementById('ns-style')) return;
const el = document.createElement('style');
el.id = 'ns-style';
el.textContent = `
.nextsteps { margin: 1.6rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
.nextsteps h4 { margin: 0 0 .5rem; font-size: .74rem; font-weight: 600;
text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.nextsteps h4 code { text-transform: none; letter-spacing: 0; }
.nextsteps ul { list-style: none; margin: 0; padding: 0; }
.nextsteps li { margin: .32rem 0; font-size: .89rem; display: flex;
gap: .5rem; align-items: baseline; }
.ns-mark { font-family: var(--mono); font-size: .8rem; color: var(--muted);
width: 1rem; flex: 0 0 1rem; }
.ns-here .ns-mark { color: var(--accent); }
.ns-here .ns-title { font-weight: 600; }
.ns-back { opacity: .72; }
.ns-hub { display: inline-block; margin-top: .7rem; font-size: .84rem; }
.nextsteps a { color: var(--accent); text-decoration: none; }
.nextsteps a:hover { text-decoration: underline; }
.ns-why { color: var(--muted); }
.ns-why::before { content: " — "; }`;
document.head.appendChild(el);
}
|