CoolFace
Apppublic

aleada/start-here

sourceHugging Faceapache-2.0updated 19d agoView on Hugging Face
0likes
tools.js152 linesDownload Raw Back to root
1/**
2 * The five tools, declared once.
3 *
4 * Each Space linked to the others from its own footer, which meant the
5 * same five URLs written out in five files — twenty-five places for a
6 * rename to break, and a Space rename is invisible from the linking
7 * side: the link keeps returning a page, and the page is a 404.
8 *
9 * So the registry lives here and is copied into each Space at deploy
10 * time, next to `progress.js`. One file to change when a tool moves.
11 *
12 * The order is the order somebody actually needs them: find a model,
13 * see how much context it can serve, learn how to run it, then check
14 * what the pack really contains before spending the download.
15 */
16
17export const TOOLS = {
18  search: {
19    space: 'model-search-that-answers',
20    title: 'Find a model that fits',
21    asks: 'which models fit my card at all',
22    // What the visitor has just learned, and why the next tool follows.
23    leadsTo: 'context',
24    takes: 'query',
25  },
26  context: {
27    space: 'what-context-fits',
28    title: 'What context will actually fit?',
29    asks: 'how long a conversation this one can serve',
30    leadsTo: 'parser',
31    takes: 'repo',
32  },
33  parser: {
34    space: 'reasoning-parser-advisor',
35    title: 'Does this model need a reasoning parser?',
36    asks: 'how to serve it without silently empty answers',
37    leadsTo: 'precision',
38    takes: 'repo',
39  },
40  precision: {
41    space: 'pack-precision-map',
42    title: 'How much of this pack is actually 4-bit?',
43    asks: 'what a quantized pack really compressed, and what it kept',
44    leadsTo: 'integrity',
45    takes: 'repo',
46  },
47  integrity: {
48    space: 'pack-integrity-check',
49    title: 'Does this pack keep what it claims?',
50    asks: 'whether anything was dropped or left undeclared',
51    leadsTo: null,
52    takes: 'repo',
53  },
54};
55
56export const ORDER = ['search', 'context', 'parser', 'precision', 'integrity'];
57
58const BASE = 'https://huggingface.co/spaces/aleada';
59const HUB = `${BASE}/start-here`;
60
61/**
62 * A link to one tool, carrying the model with it.
63 *
64 * A funnel where each step starts empty is five separate tools with
65 * links between them. `?repo=` is read on load by every page that takes
66 * one, so clicking through keeps the model you were looking at.
67 */
68export function url(key, repo = null) {
69  const t = TOOLS[key];
70  if (!t) return BASE;
71  const q = repo && t.takes === 'repo'
72    ? `?repo=${encodeURIComponent(repo)}`
73    : repo && t.takes === 'query'
74      ? `?q=${encodeURIComponent(repo)}` : '';
75  return `${BASE}/${t.space}${q}`;
76}
77
78/** The model this page was opened with, if a link carried one over. */
79export function repoFromUrl() {
80  const p = new URLSearchParams(window.location.search);
81  return (p.get('repo') || p.get('q') || '').trim() || null;
82}
83
84const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
85  ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
86
87/**
88 * The next questions, rendered under a result.
89 *
90 * The whole chain is rendered, not just the parts that come after: the
91 * current step is marked so a visitor who arrived from a link can see
92 * where they are, and the steps behind stay reachable because the order
93 * is a suggestion and not a gate.
94 *
95 * Offered only once there is an answer on screen — a next step shown
96 * before the current one is finished is clutter, and one that cannot
97 * carry the model over is a link to an empty form.
98 */
99export function nextSteps(fromKey, repo = null) {
100  const here = ORDER.indexOf(fromKey);
101  const items = ORDER.map((k, i) => {
102    const t = TOOLS[k];
103    if (i === here) {
104      // The step you are on, shown rather than hidden. A list of "other
105      // tools" tells you what exists; a chain with your place marked
106      // tells you how far along you are, which is the thing somebody
107      // arriving from a link actually wants to know.
108      return `<li class="ns-here"><span class="ns-mark">✓</span>
109        <span class="ns-title">${esc(t.title)}</span>
110        <span class="ns-why">you are here</span></li>`;
111    }
112    const ahead = i > here;
113    return `<li class="${ahead ? 'ns-next' : 'ns-back'}">
114      <span class="ns-mark">${ahead ? '→' : '·'}</span>
115      <a href="${url(k, repo)}" target="_blank" rel="noopener">${esc(t.title)}</a>
116      <span class="ns-why">${esc(t.asks)}</span></li>`;
117  }).join('');
118
119  return `<nav class="nextsteps" aria-label="The rest of the chain">
120      <h4>Your next question${repo ? ` for <code>${esc(repo)}</code>` : ''}</h4>
121      <ul>${items}</ul>
122      <a class="ns-hub" href="${HUB}${repo ? `?repo=${encodeURIComponent(repo)}` : ''}"
123         target="_blank" rel="noopener">All five, and why they are in this order →</a>
124    </nav>`;
125}
126
127/** Styles for the block above, injected once so each Space need not carry them. */
128export function ensureNextStepStyles() {
129  if (document.getElementById('ns-style')) return;
130  const el = document.createElement('style');
131  el.id = 'ns-style';
132  el.textContent = `
133.nextsteps { margin: 1.6rem 0 0; padding-top: 1rem; border-top: 1px solid var(--line); }
134.nextsteps h4 { margin: 0 0 .5rem; font-size: .74rem; font-weight: 600;
135                text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
136.nextsteps h4 code { text-transform: none; letter-spacing: 0; }
137.nextsteps ul { list-style: none; margin: 0; padding: 0; }
138.nextsteps li { margin: .32rem 0; font-size: .89rem; display: flex;
139                gap: .5rem; align-items: baseline; }
140.ns-mark { font-family: var(--mono); font-size: .8rem; color: var(--muted);
141           width: 1rem; flex: 0 0 1rem; }
142.ns-here .ns-mark { color: var(--accent); }
143.ns-here .ns-title { font-weight: 600; }
144.ns-back { opacity: .72; }
145.ns-hub { display: inline-block; margin-top: .7rem; font-size: .84rem; }
146.nextsteps a { color: var(--accent); text-decoration: none; }
147.nextsteps a:hover { text-decoration: underline; }
148.ns-why { color: var(--muted); }
149.ns-why::before { content: " — "; }`;
150  document.head.appendChild(el);
151}
152