Spaces:
Running
Running
| """The design's markup, rendered from state. | |
| This module is the visible layer of the app. Every element here is the | |
| design's own element with the design's own inline styles and token values -- | |
| not a Gradio component with CSS applied over it. Gradio owns the transport | |
| (a hidden textbox, a hidden button, one HTML sink) and nothing that is seen. | |
| Reading order matches the design top to bottom: sidebar, header, title row, | |
| stat band, filter rail, model table, right rail, footer, drawer. | |
| Two rules that are not negotiable: | |
| **Everything interpolated is escaped.** Model ids, authors, licences, red-flag | |
| text and training-data summaries all originate from model cards written by | |
| strangers on the internet. They arrive here as data and are escaped on the way | |
| into the page -- `e()` for text, `a()` for attribute values. A red flag reading | |
| `<img src=x onerror=...>` must render as characters, not as an element. | |
| **Nothing is invented.** Where the design shows a number the Atlas cannot | |
| know -- a download trend before two snapshots exist, a market price -- the slot | |
| renders an em dash or is filled with something the index actually knows. The | |
| design is the authority on layout; it is not a licence to fabricate data. | |
| """ | |
| from __future__ import annotations | |
| from bit_ui import dialogs, palette, sidebar as bit_sidebar | |
| from bit_ui.icons import icon | |
| from bit_ui.markup import DASH, a, e | |
| from .. import format as fmt | |
| from .chrome import emit | |
| # -------------------------------------------------------------------------- | |
| # Design vocabulary -- glyphs and colours, taken from the design's own maps | |
| # -------------------------------------------------------------------------- | |
| TASK_GLYPHS = { | |
| "sentiment": "◐", "ner": "⬗", "summarization": "≡", "qa": "?", | |
| "forecasting": "∿", "classification": "▦", "embedding": "⋮", | |
| "trading_signal": "⇅", "other": "·", | |
| } | |
| ASSET_GLYPHS = { | |
| "equities": "▲", "crypto": "◈", "forex": "⇄", "macro": "◍", "general": "○", | |
| } | |
| LICENSE_STYLE = { | |
| "permissive": ("◆", "var(--accent-moss-strong)", "var(--accent-moss-dim)", | |
| "Apache-2.0 / MIT and similar — commercial use allowed"), | |
| "restricted": ("◈", "var(--accent-amber-strong)", "var(--accent-amber-dim)", | |
| "Non-commercial, gated or custom terms — read before use"), | |
| "none": ("○", "var(--mute-red)", "var(--mute-red)", | |
| "No license declared — legal status unclear"), | |
| } | |
| SORT_OPTIONS = ("Downloads", "Trending", "Recently updated", "Likes") | |
| COLUMNS = ( | |
| ("Model", "left", "id"), | |
| ("Task", "left", "task"), | |
| ("Asset", "left", "asset"), | |
| ("Downloads 30d", "right", "Downloads"), | |
| ("Likes", "right", "Likes"), | |
| ("License", "left", "lic"), | |
| ("Updated", "left", "Recently updated"), | |
| ("Badges", "left", "badges"), | |
| ) | |
| GRID = ("minmax(230px,2.4fr) 104px 92px 148px 60px 116px 104px 112px") | |
| SORTABLE_COLUMNS = {"Downloads", "Likes", "Recently updated"} | |
| def task_glyph(task) -> str: | |
| return TASK_GLYPHS.get(task, "·") | |
| def asset_glyph(asset) -> str: | |
| return ASSET_GLYPHS.get(asset, "○") | |
| def license_style(bucket): | |
| return LICENSE_STYLE.get(bucket, LICENSE_STYLE["none"]) | |
| def sparkline(series, width=64, height=18) -> str: | |
| """An SVG path for a real download series, or "" when there is no series. | |
| Returning "" rather than a flat line is the point: the caller renders an | |
| em dash instead, and the user can tell the difference between "no growth" | |
| and "we have not been running long enough to know". | |
| """ | |
| if not series or len(series) < 2: | |
| return "" | |
| lo, hi = min(series), max(series) | |
| span = (hi - lo) or 1 | |
| points = [] | |
| for i, value in enumerate(series): | |
| x = i / (len(series) - 1) * width | |
| y = height - ((value - lo) / span) * (height - 2) - 1 | |
| points.append(f"{'L' if i else 'M'}{x:.1f} {y:.1f}") | |
| return " ".join(points) | |
| # -------------------------------------------------------------------------- | |
| # Header | |
| # -------------------------------------------------------------------------- | |
| def header(index, tape_rows) -> str: | |
| """Top bar and ticker tape. | |
| The design's tape scrolls live market prices and the design's status pill | |
| reads "REGIME: TRENDING". The Atlas has no price feed, and inventing one | |
| would put fabricated market data on a page whose whole argument is that | |
| unverified numbers should not be trusted. So both slots keep the design's | |
| form and carry what this app actually knows: the tape scrolls the most | |
| downloaded models in the index with their real counts, and the pill states | |
| the index's provenance. | |
| """ | |
| items = [] | |
| for row in tape_rows: | |
| series = row.get("series") | |
| change = row.get("change") | |
| if change is None: | |
| change_text, colour = DASH, "var(--fin-flat)" | |
| else: | |
| arrow = "▲" if change > 0 else ("▼" if change < 0 else "·") | |
| sign = "+" if change > 0 else "" | |
| change_text = f"{sign}{change:.1f}% {arrow}" | |
| colour = ("var(--fin-up)" if change > 0 | |
| else "var(--fin-down)" if change < 0 else "var(--fin-flat)") | |
| name = row["id"].split("/")[-1] | |
| items.append( | |
| f'<div style="display:flex;align-items:center;gap:8px;padding:0 16px;' | |
| f'border-right:1px solid var(--border-subtle)">' | |
| f'<span class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--text-secondary)">{e(name[:28])}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--text-primary)">{e(fmt.num(row.get("downloads_30d")))}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:{colour}">{e(change_text)}</span></div>' | |
| ) | |
| tape = "".join(items) * 2 if items else "" | |
| total = index.total | |
| return f""" | |
| <header class="bit-header" style="background:var(--bg-panel); | |
| border-bottom:1px solid var(--border-default)"> | |
| <div style="display:flex;align-items:center;gap:10px;padding:8px 14px; | |
| min-height:48px;flex-wrap:wrap"> | |
| <div style="display:flex;align-items:center;gap:8px;flex:0 0 auto"> | |
| <span style="display:inline-block;width:6px;height:6px; | |
| background:var(--fin-up);animation:bitPulse 2s ease-in-out infinite"></span> | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-sm); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide); | |
| color:var(--text-secondary)">Atlas</span> | |
| </div> | |
| <span style="display:flex;align-items:center;gap:8px;padding:3px 8px; | |
| border:1px solid var(--accent-amber-dim);color:var(--accent-amber-strong); | |
| flex:0 0 auto"> | |
| <span class="mono-data" style="font-size:var(--text-xs)">AUTO-INDEXED · LLM-CLASSIFIED · NOT AUDITED</span> | |
| </span> | |
| <div style="display:flex;align-items:center;gap:6px;flex:1 1 auto;min-width:0; | |
| overflow:hidden"> | |
| <span style="display:inline-block;width:6px;height:6px;background:var(--fin-up); | |
| flex:0 0 auto"></span> | |
| <span class="mono-data" style="font-size:var(--text-xs);color:var(--text-tertiary); | |
| white-space:nowrap;overflow:hidden;text-overflow:ellipsis">INDEX LOADED · {total:,} MODELS · {index.verified_count} HUMAN-VERIFIED</span> | |
| </div> | |
| <div style="flex:0 0 auto"> | |
| <a href="https://huggingface.co/{a(index.dataset_repo)}" target="_blank" | |
| rel="noopener noreferrer" title="Open the dataset on Hugging Face" | |
| class="bit-hover-connect" | |
| style="display:inline-flex;align-items:center;gap:7px;padding:4px 9px; | |
| border:1px solid var(--accent-amber-dim);background:var(--bg-raised); | |
| color:var(--text-primary);text-decoration:none"> | |
| <span style="font-size:14px;line-height:1;flex:0 0 auto">🤗</span> | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-xs); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide); | |
| white-space:nowrap;margin-top:2px">View the dataset</span> | |
| {icon('chevronRight', '11px')} | |
| </a> | |
| </div> | |
| </div> | |
| <div style="border-top:1px solid var(--border-subtle);overflow:hidden;padding:5px 0; | |
| background:var(--bg-canvas)"> | |
| <div class="bit-tape" style="display:flex;width:max-content; | |
| animation:bitTape 70s linear infinite">{tape}</div> | |
| </div> | |
| </header>""" | |
| # -------------------------------------------------------------------------- | |
| # Title row and stat band | |
| # -------------------------------------------------------------------------- | |
| def title_row(index, state, shown: int) -> str: | |
| query = state.get("q") or "" | |
| # The shared search bar: live-filters as you type and carries the ⌘K | |
| # affordance the design draws, which now opens a real palette. | |
| search = palette.search_bar( | |
| query, emit, | |
| count_label=f"{shown:,} / {index.total:,}", | |
| placeholder=f"Search {index.total:,} models…", | |
| ) | |
| stamp = index.last_indexed | |
| ago = fmt.short_ago(stamp) | |
| when = fmt.utc_stamp(stamp) | |
| indexed_text = (f"LAST INDEXED {when} · {ago} AGO" if stamp | |
| else "NEVER INDEXED — DATASET IS EMPTY") | |
| return f""" | |
| <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap"> | |
| <h1 style="font-family:var(--font-styrene);font-weight:500;font-size:var(--text-2xl); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide); | |
| line-height:var(--leading-tight);margin:0">Finance Atlas</h1> | |
| <span class="mono-data" style="font-size:var(--text-2xs);color:var(--text-tertiary); | |
| padding:3px 7px;border:1px solid var(--border-subtle)">REGISTRY · HUGGING FACE HUB</span> | |
| <div style="flex:1 1 320px;min-width:240px;max-width:480px"> | |
| {search} | |
| </div> | |
| <span style="display:inline-flex;align-items:center;gap:6px;padding:4px 8px; | |
| border:1px solid var(--border-default);background:var(--bg-panel); | |
| margin-left:auto"> | |
| <span style="display:inline-block;width:5px;height:5px;background:var(--accent-amber); | |
| animation:bitPulse 2s ease-in-out infinite"></span> | |
| <span class="mono-data" style="font-size:var(--text-2xs);color:var(--text-secondary); | |
| white-space:nowrap">{e(indexed_text)}</span> | |
| </span> | |
| </div>""" | |
| def stat_band(index) -> str: | |
| """The six-cell stat band. Every figure is computed from the index.""" | |
| total = index.total | |
| cells = ( | |
| ("Models indexed", f"{total:,}", "finance models on the Hub", | |
| "var(--text-primary)", "var(--border-default)"), | |
| ("Actively maintained", f"{index.maintained_count:,}", | |
| "recent commit or active downloads", "var(--fin-up)", "var(--fin-up)"), | |
| ("Undocumented", fmt.percent(index.undocumented_count, total), | |
| "no training data disclosed", "var(--fin-down)", "var(--fin-down)"), | |
| ("Claimed evaluations", f"{index.eval_count:,}", | |
| "card names a test set", "var(--accent-amber-strong)", "var(--accent-amber)"), | |
| ("Human-verified", f"{index.verified_count:,}", | |
| "checked by a Bit Trading researcher", | |
| "var(--text-primary)", "var(--border-default)"), | |
| ("Last indexed", fmt.short_ago(index.last_indexed), | |
| "weekly crawl", "var(--text-secondary)", "var(--border-default)"), | |
| ) | |
| body = "".join( | |
| f'<div style="padding:10px 12px;border-right:1px solid var(--border-subtle);' | |
| f'border-left:2px solid {mark}">' | |
| f'<div style="font-family:var(--font-styrene);font-weight:300;' | |
| f'font-size:var(--text-2xs);text-transform:uppercase;' | |
| f'letter-spacing:var(--tracking-wider);color:var(--text-tertiary);' | |
| f'white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{e(label)}</div>' | |
| f'<div class="mono-data" style="font-size:var(--text-2xl);line-height:1.15;' | |
| f'white-space:nowrap;color:{colour}">{e(value)}</div>' | |
| f'<div style="font-size:var(--text-2xs);color:var(--text-tertiary);' | |
| f'white-space:nowrap;overflow:hidden;text-overflow:ellipsis">{e(note)}</div>' | |
| f'</div>' | |
| for label, value, note, colour, mark in cells | |
| ) | |
| return (f'<div style="display:grid;' | |
| f'grid-template-columns:repeat(auto-fit,minmax(158px,1fr));' | |
| f'border:1px solid var(--border-default);background:var(--bg-panel)">' | |
| f'{body}</div>') | |
| # -------------------------------------------------------------------------- | |
| # Filter rail | |
| # -------------------------------------------------------------------------- | |
| def _chip(action_key: str, value: str, label: str, count: int, glyph: str, | |
| active: bool) -> str: | |
| background = "var(--accent-amber-dim)" if active else "transparent" | |
| border = "var(--accent-amber)" if active else "var(--border-default)" | |
| colour = "var(--text-primary)" if active else "var(--text-secondary)" | |
| return ( | |
| f'<button type="button" data-bit="{a(emit(action_key, value))}" ' | |
| f'aria-pressed="{"true" if active else "false"}" ' | |
| f'style="display:inline-flex;align-items:center;gap:4px;padding:2px 6px;' | |
| f'background:{background};border:1px solid {border};color:{colour};' | |
| f'cursor:pointer;font-size:var(--text-2xs);font-family:var(--font-mono)">' | |
| f'<span>{e(glyph)}</span><span>{e(label)}</span>' | |
| f'<span style="color:var(--text-tertiary)">{count}</span></button>' | |
| ) | |
| def _switch(action_key: str, label: str, on: bool) -> str: | |
| """The design system's Switch, rendered as a real button. | |
| A `gr.Checkbox` here would be a different DOM with different metrics; this | |
| is the design's own control, and it carries `role="switch"` so it is | |
| announced correctly rather than as an unlabelled button. | |
| """ | |
| track = "var(--accent-amber)" if on else "var(--border-default)" | |
| knob = "var(--stone-950)" if on else "var(--text-tertiary)" | |
| offset = "10px" if on else "1px" | |
| return ( | |
| f'<button type="button" role="switch" aria-checked="{"true" if on else "false"}" ' | |
| f'data-bit="{a(emit(action_key, "toggle"))}" ' | |
| f'style="display:flex;align-items:center;gap:7px;width:100%;' | |
| f'background:transparent;border:none;cursor:pointer;padding:0;' | |
| f'text-align:left">' | |
| f'<span aria-hidden="true" style="flex:0 0 auto;position:relative;width:20px;' | |
| f'height:11px;background:{track};display:inline-block">' | |
| f'<span style="position:absolute;top:1px;left:{offset};width:9px;height:9px;' | |
| f'background:{knob};transition:left 0.15s ease"></span></span>' | |
| f'<span style="font-size:var(--text-xs);color:var(--text-secondary)">' | |
| f'{e(label)}</span></button>' | |
| ) | |
| def _checkbox(action_key: str, label: str, on: bool) -> str: | |
| mark = "✓" if on else "" | |
| border = "var(--accent-amber)" if on else "var(--border-default)" | |
| background = "var(--accent-amber)" if on else "transparent" | |
| return ( | |
| f'<button type="button" role="checkbox" aria-checked="{"true" if on else "false"}" ' | |
| f'data-bit="{a(emit(action_key, "toggle"))}" ' | |
| f'style="display:flex;align-items:center;gap:7px;width:100%;' | |
| f'background:transparent;border:none;cursor:pointer;padding:0;text-align:left">' | |
| f'<span aria-hidden="true" style="flex:0 0 auto;width:11px;height:11px;' | |
| f'border:1px solid {border};background:{background};color:var(--stone-950);' | |
| f'font-size:8px;line-height:9px;text-align:center">{mark}</span>' | |
| f'<span style="font-size:var(--text-xs);color:var(--text-secondary)">' | |
| f'{e(label)}</span></button>' | |
| ) | |
| def _rail_section(title: str, body: str, last: bool = False) -> str: | |
| border = "" if last else "border-bottom:1px solid var(--border-subtle);" | |
| return ( | |
| f'<div style="padding:9px 10px;{border}">' | |
| f'<div style="font-family:var(--font-styrene);font-weight:300;' | |
| f'font-size:var(--text-2xs);text-transform:uppercase;' | |
| f'letter-spacing:var(--tracking-wider);color:var(--text-tertiary);' | |
| f'margin-bottom:6px">{e(title)}</div>{body}</div>' | |
| ) | |
| def filter_rail(index, state, hidden_count: int) -> str: | |
| taxonomy = index.taxonomy | |
| task_labels = taxonomy.get("task_labels", {}) | |
| asset_labels = taxonomy.get("asset_labels", {}) | |
| task_chips = "".join( | |
| _chip("task", key, task_labels.get(key, key), | |
| index.count_by("task", key), task_glyph(key), | |
| key in (state.get("tasks") or ())) | |
| for key in taxonomy.get("task_chips", ()) | |
| ) | |
| asset_chips = "".join( | |
| _chip("asset", key, asset_labels.get(key, key), | |
| index.count_by("asset_class", key), asset_glyph(key), | |
| key in (state.get("assets") or ())) | |
| for key in taxonomy.get("asset_chips", ()) | |
| ) | |
| licence_rows = [] | |
| for bucket in taxonomy.get("license_buckets", ()): | |
| glyph, colour, _, tip = license_style(bucket) | |
| active = bucket in (state.get("lic") or ()) | |
| background = "var(--accent-amber-dim)" if active else "transparent" | |
| border = "var(--accent-amber)" if active else "var(--border-default)" | |
| foreground = "var(--text-primary)" if active else colour | |
| count = index.count_by("license_bucket", bucket) | |
| licence_rows.append( | |
| f'<button type="button" data-bit="{a(emit("lic", bucket))}" ' | |
| f'title="{a(tip)}" aria-pressed="{"true" if active else "false"}" ' | |
| f'style="display:flex;align-items:center;gap:7px;padding:3px 5px;' | |
| f'background:{background};border:1px solid {border};color:{foreground};' | |
| f'cursor:pointer;text-align:left">' | |
| f'<span class="mono-data" style="font-size:var(--text-xs)">{e(glyph)}</span>' | |
| f'<span style="flex:1 1 auto;font-size:var(--text-xs)">{e(bucket)}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{count}</span></button>' | |
| ) | |
| sort_rows = [] | |
| for label in SORT_OPTIONS: | |
| active = state.get("sort") == label | |
| sort_rows.append( | |
| f'<button type="button" data-bit="{a(emit("sort", label))}" ' | |
| f'aria-pressed="{"true" if active else "false"}" ' | |
| f'style="display:flex;align-items:center;gap:7px;padding:3px 5px;' | |
| f'background:{"var(--bg-raised)" if active else "transparent"};' | |
| f'border:1px solid ' | |
| f'{"var(--accent-amber-dim)" if active else "var(--border-subtle)"};' | |
| f'color:{"var(--text-primary)" if active else "var(--text-secondary)"};' | |
| f'cursor:pointer;text-align:left;font-size:var(--text-xs)">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs)">' | |
| f'{"●" if active else "○"}</span><span>{e(label)}</span></button>' | |
| ) | |
| # The design prints "728 UNMAINTAINED HIDDEN". That number is derived from | |
| # the live filter set rather than stored, so it always matches what the | |
| # table is actually withholding. | |
| if state.get("maintained_only"): | |
| hidden_note = f"{hidden_count:,} UNMAINTAINED HIDDEN" | |
| else: | |
| hidden_note = "UNMAINTAINED SHOWN" | |
| status_body = ( | |
| f'{_switch("verified", "✓ Verified by Bit Trading", bool(state.get("verified_only")))}' | |
| f'{_switch("maintained", "Maintained only", bool(state.get("maintained_only")))}' | |
| f'<div style="display:flex;align-items:center;gap:6px;padding:5px 6px;' | |
| f'border:1px solid var(--border-subtle);background:var(--bg-raised)">' | |
| f'<span class="pixel-text" style="color:var(--text-tertiary)">†</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{e(hidden_note)}</span></div>' | |
| ) | |
| evidence_body = ( | |
| f'{_checkbox("haseval", "Has evaluation", bool(state.get("has_eval")))}' | |
| f'{_checkbox("hasdata", "Has documented training data", bool(state.get("has_data")))}' | |
| ) | |
| return f""" | |
| <aside style="width:216px;flex:0 0 216px;position:sticky;top:104px; | |
| border:1px solid var(--border-default);background:var(--bg-panel); | |
| align-self:flex-start" class="bit-rail"> | |
| <div style="display:flex;align-items:center;gap:8px;padding:8px 10px; | |
| border-bottom:1px solid var(--border-default)"> | |
| {icon('filter', '12px', 'var(--text-tertiary)')} | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-sm); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide)">Filters</span> | |
| <button type="button" data-bit="{a(emit('clear'))}" class="bit-hover-amber-text" | |
| style="margin-left:auto;background:transparent;border:none; | |
| color:var(--text-tertiary);cursor:pointer;font-family:var(--font-mono); | |
| font-size:var(--text-2xs)">CLEAR</button> | |
| </div> | |
| {_rail_section('Task', | |
| f'<div style="display:flex;flex-wrap:wrap;gap:4px">{task_chips}</div>')} | |
| {_rail_section('Asset class', | |
| f'<div style="display:flex;flex-wrap:wrap;gap:4px">{asset_chips}</div>')} | |
| {_rail_section('License', | |
| f'<div style="display:flex;flex-direction:column;gap:3px">' | |
| f'{"".join(licence_rows)}</div>')} | |
| <div style="padding:9px 10px;border-bottom:1px solid var(--border-subtle); | |
| display:flex;flex-direction:column;gap:8px"> | |
| <div style="font-family:var(--font-styrene);font-weight:300; | |
| font-size:var(--text-2xs);text-transform:uppercase; | |
| letter-spacing:var(--tracking-wider);color:var(--text-tertiary)">Status</div> | |
| {status_body} | |
| </div> | |
| <div style="padding:9px 10px;border-bottom:1px solid var(--border-subtle); | |
| display:flex;flex-direction:column;gap:7px"> | |
| <div style="font-family:var(--font-styrene);font-weight:300; | |
| font-size:var(--text-2xs);text-transform:uppercase; | |
| letter-spacing:var(--tracking-wider);color:var(--text-tertiary)">Evidence</div> | |
| {evidence_body} | |
| </div> | |
| {_rail_section('Sort', | |
| f'<div style="display:flex;flex-direction:column;gap:3px">' | |
| f'{"".join(sort_rows)}</div>', last=True)} | |
| </aside>""" | |
| # -------------------------------------------------------------------------- | |
| # Model table | |
| # -------------------------------------------------------------------------- | |
| def _badges(row) -> str: | |
| out = [] | |
| if row.get("verified"): | |
| out.append(("✓", "Verified by a Bit Trading researcher", | |
| "bit-badge-verified")) | |
| flags = row.get("red_flags") or [] | |
| if len(flags): | |
| out.append(("⚠", "; ".join(str(f) for f in flags), "bit-badge-flag")) | |
| if row.get("has_eval"): | |
| out.append(("▤", "Model card claims evaluation on a named test set", "")) | |
| if row.get("relevant") == "unclear": | |
| out.append(("?", "The classifier could not read this card confidently", | |
| "bit-badge-unclear")) | |
| return "".join( | |
| f'<span title="{a(tip)}" class="bit-badge {variant}">{e(glyph)}</span>' | |
| for glyph, tip, variant in out | |
| ) | |
| LICENSE_BUCKET_CLASS = { | |
| "permissive": "bit-tag-lic-permissive", | |
| "restricted": "bit-tag-lic-restricted", | |
| "none": "bit-tag-lic-none", | |
| } | |
| def _age_class(months) -> str: | |
| if months is None: | |
| return "bit-age-none" | |
| if months < 3: | |
| return "bit-age-fresh" | |
| if months < 12: | |
| return "bit-age-warn" | |
| return "bit-age-stale" | |
| def _table_row(row, index, selected: bool) -> str: | |
| """One row of the model index. | |
| Rendered with CSS classes rather than the design's inline styles. This is | |
| the only markup that repeats 300 times, and inline it came to 2,644 | |
| characters a row -- ~793 KB per render, 95% of the page, re-sent on every | |
| action. The design's values are unchanged; see `chrome.ATLAS_CSS`. | |
| """ | |
| model_id = row.get("id", "") | |
| labels = index.taxonomy.get("task_labels", {}) | |
| assets = index.taxonomy.get("asset_labels", {}) | |
| verified = bool(row.get("verified")) | |
| series = index.trends.get(model_id) | |
| path = sparkline(series) | |
| if path: | |
| from ..atlas import growth | |
| change = growth(series) | |
| direction = "bit-spark-up" if (change or 0) >= 0 else "bit-spark-down" | |
| spark = (f'<svg width="44" height="14" viewBox="0 0 64 18" ' | |
| f'preserveAspectRatio="none" aria-hidden="true" ' | |
| f'class="bit-spark {direction}"><path d="{a(path)}" ' | |
| f'fill="none" stroke-width="1.5"></path></svg>') | |
| else: | |
| # No second snapshot yet: there is no trend to draw, and drawing one | |
| # anyway would be inventing data. | |
| spark = (f'<span class="mono-data bit-nodata" ' | |
| f'title="Not enough weekly snapshots yet">{DASH}</span>') | |
| glyph, _colour, _border, tip = license_style(row.get("license_bucket")) | |
| lic_class = LICENSE_BUCKET_CLASS.get(row.get("license_bucket"), | |
| "bit-tag-lic-none") | |
| age_class = _age_class(fmt.months_since(row.get("last_modified"))) | |
| _, age_glyph = fmt.age_tone(row.get("last_modified")) | |
| base = row.get("base_model") or "" | |
| sub = f"from {base}" if base else "no declared base model" | |
| classes = "bit-row" | |
| if verified: | |
| classes += " bit-row-verified" | |
| if selected: | |
| classes += " bit-row-selected" | |
| return ( | |
| f'<div role="row" tabindex="0" data-bit="{a(emit("open", model_id))}" ' | |
| f'class="{classes}">' | |
| f'<div class="bit-cell bit-model">' | |
| f'<span class="mono-data bit-avatar" aria-hidden="true">' | |
| f'{e(fmt.initials(model_id))}</span>' | |
| f'<span class="bit-model-text">' | |
| f'<span class="mono-data bit-id">{e(model_id)}</span>' | |
| f'<span class="bit-sub">{e(sub)}</span></span></div>' | |
| f'<div class="bit-cell"><span class="bit-tag">' | |
| f'{e(task_glyph(row.get("task")))} ' | |
| f'{e(labels.get(row.get("task"), row.get("task")))}</span></div>' | |
| f'<div class="bit-cell"><span class="bit-tag bit-tag-asset">' | |
| f'{e(asset_glyph(row.get("asset_class")))} ' | |
| f'{e(assets.get(row.get("asset_class"), row.get("asset_class")))}</span></div>' | |
| f'<div class="bit-cell bit-dl">' | |
| f'<span class="mono-data bit-num">' | |
| f'{e(fmt.num(row.get("downloads_30d")))}</span>{spark}</div>' | |
| f'<div class="bit-cell bit-cell-r">' | |
| f'<span class="mono-data bit-likes">' | |
| f'{e(fmt.num(row.get("likes")))}</span></div>' | |
| f'<div class="bit-cell"><span class="bit-tag {lic_class}" ' | |
| f'title="{a(tip)}">{e(glyph)} ' | |
| f'{e(row.get("license") or "none")}</span></div>' | |
| f'<div class="bit-cell bit-agecell {age_class}">' | |
| f'<span class="mono-data bit-age-glyph" aria-hidden="true">' | |
| f'{e(age_glyph)}</span>' | |
| f'<span class="mono-data bit-age">' | |
| f'{e(fmt.age_label(row.get("last_modified")))}</span></div>' | |
| f'<div class="bit-cell bit-cell-end bit-badges">{_badges(row)}</div>' | |
| f'</div>' | |
| ) | |
| def model_table(index, state, rows, hidden_count: int, | |
| matched: int = None, truncated: int = 0) -> str: | |
| # `matched` is how many rows passed the filters; `rows` may be a capped | |
| # slice of them. Every count shown below reports `matched`, and the cap | |
| # is stated outright rather than quietly shrinking the number. | |
| matched = len(rows) if matched is None else matched | |
| sort = state.get("sort", "Downloads") | |
| headers = [] | |
| for label, align, key in COLUMNS: | |
| sortable = key in SORTABLE_COLUMNS | |
| colour = ("var(--accent-amber-strong)" if sort == key | |
| else "var(--text-tertiary)") | |
| action = a(emit("sort", key)) if sortable else a(emit("noop")) | |
| # Built outside the f-string: Python 3.10 does not allow a backslash | |
| # inside an f-string expression, and the Space runs 3.10. | |
| disabled = '' if sortable else 'aria-disabled="true" ' | |
| headers.append( | |
| f'<button type="button" data-bit="{action}" class="bit-hover-text" ' | |
| f'{disabled}' | |
| f'style="padding:6px 8px;background:transparent;border:none;' | |
| f'border-right:1px solid var(--border-subtle);' | |
| f'cursor:{"pointer" if sortable else "default"};text-align:{align};' | |
| f'font-family:var(--font-styrene);font-weight:300;' | |
| f'font-size:var(--text-2xs);text-transform:uppercase;' | |
| f'letter-spacing:var(--tracking-wider);color:{colour};' | |
| f'white-space:nowrap;overflow:hidden">{e(label)}</button>' | |
| ) | |
| summary = ("MAINTAINED · " if state.get("maintained_only") else "ALL · ") | |
| if state.get("verified_only"): | |
| summary += "VERIFIED · " | |
| summary += f"{matched:,} MATCHING" | |
| if not index.total: | |
| body = _empty_index_state() | |
| elif not rows: | |
| body = _empty_results_state(state, hidden_count) | |
| else: | |
| selected = state.get("sel") | |
| body = ( | |
| f'<div class="bit-scroll" style="max-height:620px;overflow-y:auto">' | |
| + "".join(_table_row(r, index, r.get("id") == selected) for r in rows) | |
| + f'<div style="display:flex;align-items:center;gap:10px;padding:7px 10px;' | |
| f'background:var(--bg-raised)">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:{"var(--accent-amber-strong)" if truncated else "var(--text-tertiary)"}">' | |
| f'{_page_note(len(rows), matched, truncated, index)}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);margin-left:auto">SCROLL FOR MORE ↓</span>' | |
| f'</div></div>' | |
| ) | |
| return f""" | |
| <section style="flex:1 1 560px;min-width:0;display:flex;flex-direction:column;gap:8px"> | |
| <div style="border:1px solid var(--border-default);background:var(--bg-panel); | |
| min-width:0" class="bit-table-scroll"> | |
| <div style="min-width:940px"> | |
| <div style="display:flex;align-items:center;gap:10px;padding:7px 10px; | |
| border-bottom:1px solid var(--border-default);background:var(--bg-raised)"> | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-sm); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide)">Model index</span> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:var(--text-tertiary)">{e(summary)}</span> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:var(--accent-amber-strong);margin-left:auto">SORTED BY {e(sort.upper())}</span> | |
| </div> | |
| <div role="row" style="display:grid;grid-template-columns:{GRID}; | |
| border-bottom:1px solid var(--border-default);background:var(--bg-panel)"> | |
| {''.join(headers)} | |
| </div> | |
| {body} | |
| </div> | |
| </div> | |
| <div style="display:flex;align-items:center;gap:8px;padding:7px 10px; | |
| border:1px solid var(--border-subtle);background:var(--bg-panel)"> | |
| <span class="pixel-text" style="color:var(--accent-amber-strong)">i</span> | |
| <span style="font-size:var(--text-xs);color:var(--text-secondary); | |
| text-wrap:pretty">Auto-indexed weekly via the Hub API and LLM | |
| classification; ✓ = human-verified by a Bit Trading researcher. Everything | |
| else is a machine's reading of a model card.</span> | |
| <a href="https://huggingface.co/datasets/{a(index.dataset_repo)}" | |
| target="_blank" rel="noopener noreferrer" style="margin-left:auto; | |
| font-family:var(--font-mono);font-size:var(--text-2xs);white-space:nowrap">METHODOLOGY ↗</a> | |
| </div> | |
| </section>""" | |
| def _page_note(shown: int, matched: int, truncated: int, index) -> str: | |
| """The footer line under the table. | |
| When the render is capped, that is stated in the line rather than left for | |
| the user to infer from a row count that stops at a round number. | |
| """ | |
| if truncated: | |
| return (f"SHOWING FIRST {shown:,} OF {matched:,} MATCHING " | |
| f"— NARROW THE FILTERS TO SEE THE REST") | |
| return (f"SHOWING {shown:,} OF {index.maintained_count:,} MAINTAINED " | |
| f"· {index.total:,} INDEXED") | |
| def _empty_index_state() -> str: | |
| """Shown when the dataset itself is empty or unreachable.""" | |
| return ( | |
| f'<div style="padding:56px 24px;display:flex;flex-direction:column;' | |
| f'align-items:center;gap:12px;text-align:center">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">NO INDEX</span>' | |
| f'<div style="font-family:var(--font-styrene);font-size:var(--text-xl);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide)">' | |
| f'The index has not been built yet</div>' | |
| f'<div style="max-width:430px;color:var(--text-secondary);' | |
| f'text-wrap:pretty">This Space reads a dataset that is empty or ' | |
| f'unreachable. The weekly indexing job populates it; until it has run ' | |
| f'once there is nothing to show.</div></div>' | |
| ) | |
| def _empty_results_state(state, hidden_count: int) -> str: | |
| query = state.get("q") or "" | |
| echo = f'"{query}"' if query else "these filters" | |
| graveyard = "" | |
| if state.get("maintained_only") and hidden_count: | |
| graveyard = ( | |
| f'<button type="button" data-bit="{a(emit("graveyard"))}" ' | |
| f'class="bit-hover-amber" style="padding:4px 9px;background:transparent;' | |
| f'border:1px solid var(--border-default);color:var(--text-secondary);' | |
| f'cursor:pointer;font-size:var(--text-xs)">Include {hidden_count:,} ' | |
| f'unmaintained</button>' | |
| ) | |
| return ( | |
| f'<div style="padding:56px 24px;display:flex;flex-direction:column;' | |
| f'align-items:center;gap:12px;text-align:center">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">0 RESULTS</span>' | |
| f'<div style="font-family:var(--font-styrene);font-size:var(--text-xl);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide)">' | |
| f'Nothing matches {e(echo)}</div>' | |
| f'<div style="max-width:430px;color:var(--text-secondary);text-wrap:pretty">' | |
| f'If a model exists on the Hub and is not here, that is a gap in our ' | |
| f'harvest terms or our classifier — not proof it does not exist.</div>' | |
| f'<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center">' | |
| f'<button type="button" data-bit="{a(emit("clear"))}" class="bit-hover-amber" ' | |
| f'style="padding:4px 9px;background:transparent;' | |
| f'border:1px solid var(--border-default);color:var(--text-secondary);' | |
| f'cursor:pointer;font-size:var(--text-xs)">Clear filters</button>' | |
| f'{graveyard}</div></div>' | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Right rail | |
| # -------------------------------------------------------------------------- | |
| def _panel(title: str, body: str, meta: str = "") -> str: | |
| meta_html = (f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);margin-left:auto">{e(meta)}</span>' | |
| if meta else "") | |
| return ( | |
| f'<div style="border:1px solid var(--border-default);' | |
| f'background:var(--bg-panel)">' | |
| f'<div style="display:flex;align-items:center;gap:8px;padding:7px 10px;' | |
| f'border-bottom:1px solid var(--border-default)">' | |
| f'<span style="font-family:var(--font-styrene);font-size:var(--text-sm);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide)">' | |
| f'{e(title)}</span>{meta_html}</div>{body}</div>' | |
| ) | |
| def trending_panel(index, rows) -> str: | |
| """Top movers by real week-over-week download growth. | |
| Before two snapshots exist there is no growth to rank by, so the panel | |
| says that instead of ranking on a fabricated number. | |
| """ | |
| if not rows: | |
| body = ( | |
| f'<div style="padding:14px 10px;display:flex;flex-direction:column;' | |
| f'gap:6px;text-align:center">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">NO TREND YET</span>' | |
| f'<span style="font-size:var(--text-2xs);color:var(--text-tertiary);' | |
| f'text-wrap:pretty">Trends need at least two weekly snapshots. ' | |
| f'The first crawl has run; the next one makes this panel live.</span>' | |
| f'</div>' | |
| ) | |
| return _panel("Trending this quarter", body) | |
| items = [] | |
| for rank, (model_id, change, series) in enumerate(rows, start=1): | |
| path = sparkline(series) | |
| spark = (f'<svg width="36" height="13" viewBox="0 0 64 18" ' | |
| f'preserveAspectRatio="none" aria-hidden="true" ' | |
| f'style="flex:0 0 auto"><path d="{a(path)}" style="fill:none;' | |
| f'stroke:var(--fin-up);stroke-width:1.5"></path></svg>') | |
| items.append( | |
| f'<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;' | |
| f'border-bottom:1px solid var(--border-subtle)">' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);flex:0 0 auto">{rank:02d}</span>' | |
| f'<span class="mono-data" style="flex:1 1 auto;min-width:0;' | |
| f'font-size:var(--text-xs);color:var(--text-primary);white-space:nowrap;' | |
| f'overflow:hidden;text-overflow:ellipsis" title="{a(model_id)}">' | |
| f'{e(model_id.split("/")[-1])}</span>{spark}' | |
| f'<span class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--fin-up);flex:0 0 auto">+{change:.0f}%</span></div>' | |
| ) | |
| return _panel("Trending this quarter", "".join(items)) | |
| def lineage_panel(index) -> str: | |
| root, kids = index.spotlight() | |
| if not root or not kids: | |
| body = (f'<div style="padding:14px 10px;text-align:center">' | |
| f'<span style="font-size:var(--text-2xs);color:var(--text-tertiary)">' | |
| f'No declared lineage in the index yet.</span></div>') | |
| return _panel("Lineage spotlight", body) | |
| shown = kids[:4] | |
| rows = [] | |
| for position, child in enumerate(shown): | |
| last = position == len(shown) - 1 and len(kids) <= len(shown) | |
| row = index.by_id.get(child, {}) | |
| rows.append( | |
| f'<div style="display:flex;align-items:center;gap:6px;padding-left:10px">' | |
| f'<span class="mono-data" aria-hidden="true" style="font-size:var(--text-2xs);' | |
| f'color:var(--border-strong)">{"└─" if last else "├─"}</span>' | |
| f'<span class="mono-data" style="flex:1 1 auto;min-width:0;' | |
| f'font-size:var(--text-2xs);color:var(--text-secondary);white-space:nowrap;' | |
| f'overflow:hidden;text-overflow:ellipsis" title="{a(child)}">' | |
| f'{e(child)}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);flex:0 0 auto">' | |
| f'{e(fmt.compact(row.get("downloads_30d")))}</span></div>' | |
| ) | |
| if len(kids) > len(shown): | |
| rows.append( | |
| f'<div style="display:flex;align-items:center;gap:6px;padding-left:10px">' | |
| f'<span class="mono-data" aria-hidden="true" style="font-size:var(--text-2xs);' | |
| f'color:var(--border-strong)">└─</span>' | |
| f'<span class="mono-data" style="flex:1 1 auto;font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">+ {len(kids) - len(shown)} more descendants</span>' | |
| f'</div>' | |
| ) | |
| body = ( | |
| f'<div style="padding:10px">' | |
| f'<div class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--accent-amber-strong);border:1px solid var(--accent-amber-dim);' | |
| f'padding:3px 6px;display:inline-block">{e(root)}</div>' | |
| f'<div style="display:flex;flex-direction:column;margin-top:4px">' | |
| f'{"".join(rows)}</div></div>' | |
| ) | |
| return _panel("Lineage spotlight", body, f"{len(kids)} DESCENDANTS") | |
| def graveyard_panel(index) -> str: | |
| return f""" | |
| <div style="border:1px solid var(--border-default);background:var(--bg-raised)"> | |
| <div style="display:flex;flex-direction:column;align-items:center;gap:6px; | |
| padding:14px 12px;border-top:3px solid var(--border-strong)"> | |
| <span class="mono-data" aria-hidden="true" | |
| style="font-size:var(--text-lg);color:var(--text-tertiary)">†</span> | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-sm); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide); | |
| color:var(--text-secondary)">The Graveyard</span> | |
| <span class="mono-data" style="font-size:var(--text-xl); | |
| color:var(--text-primary)">{index.unmaintained_count:,}</span> | |
| <span style="font-size:var(--text-2xs);color:var(--text-tertiary); | |
| text-align:center;text-wrap:pretty">no commit in 12+ months and no | |
| meaningful download activity</span> | |
| <button type="button" data-bit="{a(emit('graveyard'))}" class="bit-hover-amber" | |
| style="margin-top:2px;padding:3px 8px;background:transparent; | |
| border:1px solid var(--border-default);color:var(--text-secondary); | |
| cursor:pointer;font-family:var(--font-mono);font-size:var(--text-2xs)">SHOW THEM →</button> | |
| </div> | |
| </div>""" | |
| def suggest_panel(state) -> str: | |
| note = state.get("suggest_note") or "REVIEWED WEEKLY" | |
| colour = ("var(--accent-moss-strong)" if state.get("suggest_ok") | |
| else "var(--text-tertiary)") | |
| body = ( | |
| f'<div style="padding:10px;display:flex;flex-direction:column;gap:7px">' | |
| f'<label for="bit-suggest" class="bit-sr-only">Suggest a model by id</label>' | |
| f'<input id="bit-suggest" data-bit-input="suggestbox" ' | |
| f'value="{a(state.get("suggest") or "")}" placeholder="author/model-name" ' | |
| f'style="width:100%;padding:5px 7px;background:var(--bg-sunken);' | |
| f'border:1px solid var(--border-default);outline:none;' | |
| f'font-family:var(--font-mono);font-size:var(--text-xs);' | |
| f'color:var(--text-primary)" />' | |
| f'<div style="display:flex;align-items:center;gap:6px">' | |
| f'<button type="button" data-bit="{a(emit("suggest"))}" ' | |
| f'class="bit-hover-amber" style="padding:4px 10px;background:transparent;' | |
| f'border:1px solid var(--border-default);color:var(--text-secondary);' | |
| f'cursor:pointer;font-family:var(--font-styrene);font-size:var(--text-xs);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide)">Submit</button>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);color:{colour}">' | |
| f'{e(note)}</span></div></div>' | |
| ) | |
| return _panel("Suggest a model", body) | |
| def right_rail(index, state, trending_rows) -> str: | |
| return ( | |
| f'<aside style="width:268px;flex:0 0 268px;display:flex;' | |
| f'flex-direction:column;gap:12px;min-width:0" class="bit-rail-right">' | |
| f'{trending_panel(index, trending_rows)}' | |
| f'{lineage_panel(index)}' | |
| f'{graveyard_panel(index)}' | |
| f'{suggest_panel(state)}' | |
| f'</aside>' | |
| ) | |
| # -------------------------------------------------------------------------- | |
| # Footer | |
| # -------------------------------------------------------------------------- | |
| FOOT_PRODUCTS = ("News Scout", "Sentiment Deep-Dive", "Earnings Analyzer", | |
| "Filing Digester", "Forecaster", "Regime Detector", | |
| "Model Leaderboard", "Backtest Lab", "Strategy Graveyard", | |
| "Paper-Trading Arena", "Signal Aggregator", | |
| "Portfolio Risk Analyzer") | |
| def footer(index, links: dict) -> str: | |
| def entries(names, columns=1): | |
| out = [] | |
| for name in names: | |
| href = links.get(name, "") | |
| if href: | |
| out.append(f'<a href="{a(href)}" target="_blank" ' | |
| f'rel="noopener noreferrer" style="color:var(--text-secondary);' | |
| f'font-size:var(--text-xs)">{e(name)}</a>') | |
| else: | |
| out.append(f'<span style="color:var(--text-tertiary);' | |
| f'font-size:var(--text-xs)">{e(name)}</span>') | |
| return "".join(out) | |
| resources = ("Dataset", "Backtest Lab", "Hugging Face org") | |
| stamp = fmt.utc_stamp(index.last_indexed) | |
| return f""" | |
| <footer style="border:1px solid var(--border-default);background:var(--bg-panel)"> | |
| <div style="display:grid;grid-template-columns:1.1fr 0.8fr 0.8fr 1.6fr;gap:0" | |
| class="bit-foot-grid"> | |
| <div style="padding:14px;border-right:1px solid var(--border-subtle)"> | |
| <div style="font-family:var(--font-styrene);font-size:var(--text-2xs); | |
| letter-spacing:var(--tracking-wider);text-transform:uppercase; | |
| color:var(--text-tertiary);margin-bottom:8px">Products</div> | |
| <div style="display:grid;grid-template-columns:1fr 1fr;gap:3px 12px"> | |
| {entries(FOOT_PRODUCTS)} | |
| </div> | |
| </div> | |
| <div style="padding:14px;border-right:1px solid var(--border-subtle)"> | |
| <div style="font-family:var(--font-styrene);font-size:var(--text-2xs); | |
| letter-spacing:var(--tracking-wider);text-transform:uppercase; | |
| color:var(--text-tertiary);margin-bottom:8px">Resources</div> | |
| <div style="display:flex;flex-direction:column;gap:3px">{entries(resources)}</div> | |
| </div> | |
| <div style="padding:14px;border-right:1px solid var(--border-subtle)"> | |
| <div style="font-family:var(--font-styrene);font-size:var(--text-2xs); | |
| letter-spacing:var(--tracking-wider);text-transform:uppercase; | |
| color:var(--text-tertiary);margin-bottom:8px">Company</div> | |
| <div style="display:flex;flex-direction:column;gap:3px"> | |
| {entries(("Hugging Face org",))} | |
| </div> | |
| </div> | |
| <div style="padding:14px;background:var(--bg-raised)"> | |
| <div style="display:flex;align-items:center;gap:6px;margin-bottom:8px"> | |
| <span class="pixel-text" style="color:var(--accent-amber-strong)">⚠</span> | |
| <span style="font-family:var(--font-styrene);font-size:var(--text-2xs); | |
| letter-spacing:var(--tracking-wider);text-transform:uppercase; | |
| color:var(--accent-amber-strong)">Disclaimer</span> | |
| </div> | |
| <div class="styrene-text" style="font-size:var(--text-xs); | |
| text-transform:uppercase;letter-spacing:var(--tracking-wide); | |
| color:var(--text-secondary);line-height:var(--leading-normal); | |
| text-wrap:pretty">The Bit Trading Company publishes research tools and | |
| educational software. Nothing here is financial advice. Inclusion in this | |
| index is not an endorsement, and every classification is a machine's | |
| reading of a model card.</div> | |
| </div> | |
| </div> | |
| <div style="display:flex;align-items:center;gap:14px;padding:8px 14px; | |
| border-top:1px solid var(--border-subtle);flex-wrap:wrap"> | |
| <span style="display:inline-block;width:6px;height:6px;background:var(--fin-up); | |
| animation:bitPulse 2s ease-in-out infinite"></span> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:var(--text-tertiary)">{index.total:,} MODELS INDEXED · | |
| {index.verified_count} VERIFIED · LAST BUILD {e(stamp)}</span> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:var(--text-tertiary);margin-left:auto">© 2026 THE BIT TRADING COMPANY</span> | |
| </div> | |
| </footer>""" | |
| # -------------------------------------------------------------------------- | |
| # Detail drawer | |
| # -------------------------------------------------------------------------- | |
| def _drawer_fields(row, index) -> str: | |
| labels = index.taxonomy.get("task_labels", {}) | |
| assets = index.taxonomy.get("asset_labels", {}) | |
| _, lic_colour, _, _ = license_style(row.get("license_bucket")) | |
| age_colour, _ = fmt.age_tone(row.get("last_modified")) | |
| has_eval = bool(row.get("has_eval")) | |
| has_data = bool((row.get("training_data_summary") or "").strip()) | |
| fields = ( | |
| ("Task", labels.get(row.get("task"), row.get("task")), "var(--text-primary)"), | |
| ("Asset class", assets.get(row.get("asset_class"), row.get("asset_class")), | |
| "var(--text-primary)"), | |
| ("Parameters", row.get("params") or DASH, "var(--text-primary)"), | |
| ("License", row.get("license") or "none declared", lic_colour), | |
| ("Downloads 30d", fmt.num(row.get("downloads_30d")), "var(--text-primary)"), | |
| ("Downloads all-time", fmt.num(row.get("downloads")), "var(--text-primary)"), | |
| ("Likes", fmt.num(row.get("likes")), "var(--text-primary)"), | |
| ("Last commit", fmt.age_label(row.get("last_modified")), age_colour), | |
| ("Library", row.get("library") or DASH, "var(--text-secondary)"), | |
| ("Weights", "present" if row.get("has_weights") else "none found", | |
| "var(--accent-moss-strong)" if row.get("has_weights") else "var(--mute-red)"), | |
| ("Evaluation", "claimed" if has_eval else "none found", | |
| "var(--accent-moss-strong)" if has_eval else "var(--mute-red)"), | |
| ("Training data", "documented" if has_data else "undocumented", | |
| "var(--accent-moss-strong)" if has_data else "var(--mute-red)"), | |
| ("Indexed", fmt.iso_date(row.get("indexed_at")), "var(--text-secondary)"), | |
| ) | |
| return "".join( | |
| f'<div style="padding:6px 8px;border-right:1px solid var(--border-subtle);' | |
| f'border-bottom:1px solid var(--border-subtle);min-width:0">' | |
| f'<div style="font-family:var(--font-styrene);font-weight:300;' | |
| f'font-size:var(--text-2xs);text-transform:uppercase;' | |
| f'letter-spacing:var(--tracking-wider);color:var(--text-tertiary);' | |
| f'white-space:nowrap">{e(key)}</div>' | |
| f'<div class="mono-data" style="font-size:var(--text-xs);color:{colour};' | |
| f'white-space:nowrap;overflow:hidden;text-overflow:ellipsis" ' | |
| f'title="{a(value)}">{e(value)}</div></div>' | |
| for key, value, colour in fields | |
| ) | |
| def _drawer_lineage(row, index) -> str: | |
| from .. import lineage as lin | |
| model_id = row.get("id", "") | |
| parent = row.get("base_model") or "" | |
| kin = lin.siblings(model_id, index.parents, index.children)[:4] | |
| kids = lin.direct_children(model_id, index.children) | |
| if not parent: | |
| parent_html = (f'<div class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--text-tertiary)">no declared base model</div>') | |
| else: | |
| indexed = parent in index.by_id | |
| note = "" if indexed else " (not indexed)" | |
| parent_html = ( | |
| f'<div class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--text-tertiary)">fine-tuned from</div>' | |
| f'<div class="mono-data" style="font-size:var(--text-xs);' | |
| f'color:var(--accent-amber-strong);border:1px solid var(--accent-amber-dim);' | |
| f'padding:2px 6px;display:inline-block;margin:4px 0;word-break:break-all">' | |
| f'{e(parent)}{e(note)}</div>' | |
| ) | |
| entries = [(k, "sibling") for k in kin] + [(k, "child") for k in kids[:4]] | |
| rows = [] | |
| for position, (other, relation) in enumerate(entries): | |
| last = position == len(entries) - 1 | |
| other_row = index.by_id.get(other, {}) | |
| tag = f"{relation} · {fmt.compact(other_row.get('downloads_30d'))} dl" | |
| flagged = bool(len(other_row.get("red_flags") or [])) | |
| colour = ("var(--accent-amber-strong)" if flagged else "var(--text-secondary)") | |
| rows.append( | |
| f'<div style="display:flex;align-items:center;gap:6px;padding-left:10px">' | |
| f'<span class="mono-data" aria-hidden="true" style="font-size:var(--text-2xs);' | |
| f'color:var(--border-strong)">{"└─" if last else "├─"}</span>' | |
| f'<span class="mono-data" style="flex:1 1 auto;min-width:0;' | |
| f'font-size:var(--text-2xs);color:{colour};white-space:nowrap;' | |
| f'overflow:hidden;text-overflow:ellipsis" title="{a(other)}">' | |
| f'{e(other)}</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary)">{e(tag)}{" ⚠" if flagged else ""}</span></div>' | |
| ) | |
| if not rows: | |
| rows.append(f'<div class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:var(--text-tertiary);padding-left:10px">' | |
| f'no indexed relatives</div>') | |
| return ( | |
| f'<div style="border:1px solid var(--border-default)">' | |
| f'<div style="padding:6px 9px;border-bottom:1px solid var(--border-default);' | |
| f'font-family:var(--font-styrene);font-size:var(--text-xs);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide);' | |
| f'color:var(--text-secondary)">Data lineage</div>' | |
| f'<div style="padding:9px">{parent_html}' | |
| f'<div style="display:flex;flex-direction:column;margin-top:2px">' | |
| f'{"".join(rows)}</div></div></div>' | |
| ) | |
| def _drawer_trend(row, index) -> str: | |
| """The weekly download chart, or an honest note that there is no series.""" | |
| series = index.trends.get(row.get("id")) | |
| if not series or len(series) < 2: | |
| return ( | |
| f'<div style="border:1px solid var(--border-default)">' | |
| f'<div style="padding:6px 9px;border-bottom:1px solid var(--border-default);' | |
| f'font-family:var(--font-styrene);font-size:var(--text-xs);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide);' | |
| f'color:var(--text-secondary)">Downloads · weekly snapshots</div>' | |
| f'<div style="padding:14px 9px;text-align:center">' | |
| f'<span style="font-size:var(--text-2xs);color:var(--text-tertiary);' | |
| f'text-wrap:pretty">Not enough snapshots yet. A trend needs at least ' | |
| f'two weekly crawls; this model has {len(series or [1])}.</span></div></div>' | |
| ) | |
| width, height = 480, 112 | |
| lo, hi = min(series), max(series) | |
| span = (hi - lo) or 1 | |
| points = [] | |
| for i, value in enumerate(series): | |
| x = i / (len(series) - 1) * width | |
| y = height - ((value - lo) / span) * (height - 14) - 7 | |
| points.append(f"{'L' if i else 'M'}{x:.1f} {y:.1f}") | |
| line = " ".join(points) | |
| area = f"{line} L{width} {height} L0 {height} Z" | |
| from ..atlas import growth | |
| change = growth(series) | |
| trend_text = DASH if change is None else f"{'+' if change >= 0 else ''}{change:.1f}% / {len(series)}W" | |
| trend_colour = ("var(--fin-up)" if (change or 0) >= 0 else "var(--fin-down)") | |
| grid = "".join( | |
| f'<line x1="0" y1="{height * f:.1f}" x2="{width}" y2="{height * f:.1f}" ' | |
| f'style="stroke:var(--border-subtle);stroke-width:1"></line>' | |
| for f in (0.15, 0.4, 0.65, 0.9) | |
| ) | |
| return ( | |
| f'<div style="border:1px solid var(--border-default)">' | |
| f'<div style="display:flex;align-items:center;gap:8px;padding:6px 9px;' | |
| f'border-bottom:1px solid var(--border-default)">' | |
| f'<span style="font-family:var(--font-styrene);font-size:var(--text-xs);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide);' | |
| f'color:var(--text-secondary)">Downloads · weekly snapshots</span>' | |
| f'<span class="mono-data" style="font-size:var(--text-2xs);' | |
| f'color:{trend_colour};margin-left:auto">{e(trend_text)}</span></div>' | |
| f'<div style="padding:10px 9px 6px">' | |
| f'<svg viewBox="0 0 {width} {height + 18}" width="100%" height="120" ' | |
| f'preserveAspectRatio="none" role="img" ' | |
| f'aria-label="Weekly downloads, {e(trend_text)}" style="display:block">' | |
| f'{grid}<path d="{a(area)}" style="fill:var(--accent-amber);opacity:0.13"></path>' | |
| f'<path d="{a(line)}" style="fill:none;stroke:var(--accent-amber-strong);' | |
| f'stroke-width:2"></path></svg>' | |
| f'<div style="display:flex;justify-content:space-between">' | |
| f'<span class="pixel-text" style="color:var(--text-tertiary)">W-{len(series) - 1}</span>' | |
| f'<span class="pixel-text" style="color:var(--text-tertiary)">W-0</span>' | |
| f'</div></div></div>' | |
| ) | |
| def _drawer_notes(row) -> str: | |
| verified = bool(row.get("verified")) | |
| flags = [str(f) for f in (row.get("red_flags") or [])] | |
| unclear = row.get("relevant") == "unclear" | |
| if verified: | |
| title, colour, border = ("Verification notes", "var(--text-secondary)", | |
| "var(--border-default)") | |
| notes = [("✓", "var(--accent-moss-strong)", | |
| "Promoted to verified by a Bit Trading researcher. The entry in " | |
| "verified.json is a human's judgement, not the classifier's.")] | |
| notes += [("⚠", "var(--accent-amber-strong)", f) for f in flags] | |
| elif flags: | |
| title, colour, border = ("Red flags", "var(--accent-amber-strong)", | |
| "var(--accent-amber-dim)") | |
| notes = [("⚠", "var(--accent-amber-strong)", f) for f in flags] | |
| notes.append(("·", "var(--text-tertiary)", | |
| "Auto-indexed only. No Bit Trading researcher has reproduced " | |
| "anything on this card.")) | |
| else: | |
| title, colour, border = ("Indexer notes", "var(--text-secondary)", | |
| "var(--border-default)") | |
| notes = [("·", "var(--text-tertiary)", | |
| "Auto-indexed only. The classifier found no red flags, and no " | |
| "human has verified this either.")] | |
| notes.append(("·", "var(--text-tertiary)", | |
| "Model card claims an evaluation on a named test set." | |
| if row.get("has_eval") else | |
| "Model card has no evaluation section we could find.")) | |
| if unclear: | |
| notes.append(("?", "var(--text-tertiary)", | |
| "The classifier could not read this card confidently, so the " | |
| "task and asset class below are low-confidence guesses.")) | |
| body = "".join( | |
| f'<div style="display:flex;align-items:flex-start;gap:7px">' | |
| f'<span class="mono-data" aria-hidden="true" style="font-size:var(--text-xs);' | |
| f'color:{tone};flex:0 0 auto">{e(glyph)}</span>' | |
| f'<span style="font-size:var(--text-xs);color:var(--text-secondary);' | |
| f'text-wrap:pretty">{e(text)}</span></div>' | |
| for glyph, tone, text in notes | |
| ) | |
| return ( | |
| f'<div style="border:1px solid {border};background:var(--bg-raised)">' | |
| f'<div style="padding:6px 9px;border-bottom:1px solid var(--border-subtle);' | |
| f'font-family:var(--font-styrene);font-size:var(--text-xs);' | |
| f'text-transform:uppercase;letter-spacing:var(--tracking-wide);' | |
| f'color:{colour}">{e(title)}</div>' | |
| f'<div style="padding:9px;display:flex;flex-direction:column;gap:6px">' | |
| f'{body}</div></div>' | |
| ) | |
| def backtestable(row, taxonomy) -> bool: | |
| """Whether to offer the Backtest Lab hand-off for this model. | |
| Narrower than the design's own rule, deliberately. The design shows the | |
| link for any forecasting or trading-signal model; this shows it only for | |
| forecasting models in an adapter family the Backtest Lab can actually load | |
| (chronos, timesfm). A link the destination Space cannot honour is worse | |
| than no link. | |
| """ | |
| if row.get("task") != "forecasting": | |
| return False | |
| families = tuple(taxonomy.get("backtestable_families", ())) | |
| return (row.get("adapter_family") or "") in families | |
| def drawer(row, index) -> str: | |
| if not row: | |
| return "" | |
| model_id = row.get("id", "") | |
| verified = bool(row.get("verified")) | |
| labels = index.taxonomy.get("task_labels", {}) | |
| assets = index.taxonomy.get("asset_labels", {}) | |
| lic_glyph, lic_colour, lic_border, _ = license_style(row.get("license_bucket")) | |
| summary = (row.get("training_data_summary") or "").strip() | |
| if not summary: | |
| summary = ("The model card does not describe the training data. That is " | |
| "the classifier's finding, not an oversight in this page.") | |
| if backtestable(row, index.taxonomy): | |
| url = index.taxonomy.get("backtest_space_url", "") | |
| backtest = ( | |
| f'<a href="{a(url)}" target="_blank" rel="noopener noreferrer" ' | |
| f'class="bit-hover-cta" style="display:inline-flex;align-items:center;' | |
| f'padding:6px 11px;background:var(--accent-amber);' | |
| f'border:1px solid var(--accent-amber);color:var(--stone-950);' | |
| f'text-decoration:none;font-family:var(--font-styrene);' | |
| f'font-size:var(--text-sm);text-transform:uppercase;' | |
| f'letter-spacing:var(--tracking-wide)">Backtest this model →</a>' | |
| ) | |
| else: | |
| backtest = "" | |
| return f""" | |
| <div data-bit="{a(emit('close'))}" style="position:fixed;inset:0;z-index:70; | |
| background:rgba(10,10,8,0.55);display:flex;justify-content:flex-end; | |
| animation:bitFade 0.3s ease"> | |
| <div role="dialog" aria-modal="true" aria-label="Model record: {a(model_id)}" | |
| data-bit="{a(emit('noop'))}" | |
| class="bit-scroll bit-drawer" | |
| style="width:min(520px,100%);height:100%;overflow-y:auto; | |
| background:var(--bg-panel);border-left:1px solid var(--border-strong); | |
| animation:bitSlide 0.3s ease"> | |
| <div style="position:sticky;top:0;z-index:2;display:flex;align-items:center; | |
| gap:8px;padding:9px 12px;background:var(--bg-raised); | |
| border-bottom:1px solid var(--border-default)"> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:var(--text-tertiary)">MODEL RECORD</span> | |
| <span class="mono-data" style="font-size:var(--text-2xs); | |
| color:{"var(--accent-amber-strong)" if verified else "var(--text-tertiary)"}; | |
| border:1px solid | |
| {"var(--accent-amber-dim)" if verified else "var(--border-default)"}; | |
| padding:1px 5px">{"✓ VERIFIED BY BIT TRADING" if verified else "AUTO-INDEXED"}</span> | |
| <button type="button" data-bit="{a(emit('close'))}" title="Close" | |
| class="bit-hover-strong" style="margin-left:auto;background:transparent; | |
| border:1px solid var(--border-default);color:var(--text-tertiary); | |
| width:22px;height:22px;display:flex;align-items:center; | |
| justify-content:center;cursor:pointer">{icon('close', '11px')}</button> | |
| </div> | |
| <div style="padding:12px;display:flex;flex-direction:column;gap:12px"> | |
| <div style="display:flex;align-items:flex-start;gap:10px"> | |
| <span class="mono-data" aria-hidden="true" style="flex:0 0 auto;width:34px; | |
| height:34px;display:flex;align-items:center;justify-content:center; | |
| background:{"var(--accent-amber)" if verified else "var(--stone-700)"}; | |
| color:{"var(--stone-950)" if verified else "var(--text-secondary)"}; | |
| font-size:11px">{e(fmt.initials(model_id))}</span> | |
| <div style="min-width:0;flex:1 1 auto"> | |
| <div class="mono-data" style="font-size:var(--text-lg); | |
| color:var(--text-primary);word-break:break-all">{e(model_id)}</div> | |
| <div style="display:flex;gap:5px;flex-wrap:wrap;margin-top:5px"> | |
| <span style="flex:0 0 auto;white-space:nowrap;padding:1px 5px; | |
| border:1px solid var(--border-default);color:var(--text-secondary); | |
| font-family:var(--font-mono);font-size:var(--text-2xs)"> | |
| {e(task_glyph(row.get('task')))} {e(labels.get(row.get('task'), row.get('task')))}</span> | |
| <span style="flex:0 0 auto;white-space:nowrap;padding:1px 5px; | |
| border:1px solid var(--border-subtle);background:var(--bg-raised); | |
| color:var(--text-secondary);font-family:var(--font-mono); | |
| font-size:var(--text-2xs)"> | |
| {e(asset_glyph(row.get('asset_class')))} {e(assets.get(row.get('asset_class'), row.get('asset_class')))}</span> | |
| <span style="flex:0 0 auto;white-space:nowrap;padding:1px 5px; | |
| border:1px solid {lic_border};color:{lic_colour}; | |
| font-family:var(--font-mono);font-size:var(--text-2xs)"> | |
| {e(lic_glyph)} {e(row.get('license') or 'none')}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div style="color:var(--text-secondary);font-size:var(--text-xs); | |
| line-height:var(--leading-normal);text-wrap:pretty; | |
| border-left:2px solid var(--border-default);padding-left:8px">{e(summary)}</div> | |
| <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); | |
| border:1px solid var(--border-default)">{_drawer_fields(row, index)}</div> | |
| {_drawer_lineage(row, index)} | |
| {_drawer_trend(row, index)} | |
| {_drawer_notes(row)} | |
| <div style="display:flex;gap:6px;flex-wrap:wrap"> | |
| <a href="https://huggingface.co/{a(model_id)}" target="_blank" | |
| rel="noopener noreferrer" class="bit-hover-amber" | |
| style="display:inline-flex;align-items:center;padding:6px 11px; | |
| background:transparent;border:1px solid var(--border-default); | |
| color:var(--text-secondary);text-decoration:none; | |
| font-size:var(--text-xs)">View on HF ↗</a> | |
| {backtest} | |
| </div> | |
| </div> | |
| </div> | |
| </div>""" | |
| # -------------------------------------------------------------------------- | |
| # Page | |
| # -------------------------------------------------------------------------- | |
| def page(index, state, view) -> str: | |
| """The whole page, assembled from one render pass over `view`. | |
| `view` carries everything the renderers need that is derived rather than | |
| stored -- the filtered rows, the hidden count, the trending list -- so | |
| each section is a pure function of (index, state, view) and the filtering | |
| runs once per request rather than once per section. | |
| ## No forced `vh` on the root | |
| The root deliberately has no `min-height:100vh`. huggingface.co embeds a | |
| Space in an `<iframe scrolling="no">` and sizes it from the app's reported | |
| content height, so a root that forces its own height from `vh` feeds back: | |
| iframe grows -> 100vh grows -> content reports taller -> iframe grows. It | |
| ratchets, and the visitor sees a page that scrolls forever into empty | |
| space. Measured here at 3890px against ~2100px of real content. | |
| The canvas background is painted on `html, body` and `.gradio-container` | |
| in bit_ui.theme instead, which is what the `100vh` was really for. | |
| `theme.find_forced_vh()` and the test suite keep it out. | |
| """ | |
| selected = index.by_id.get(state.get("sel")) if state.get("sel") else None | |
| matched = view.get("matched", len(view["rows"])) | |
| truncated = view.get("truncated", 0) | |
| # Derived defensively, like the counts: a caller that only cares about the | |
| # table can pass `rows` and get a correct page with the shared nav. | |
| from bit_ui import nav as bit_nav | |
| navigation = view.get("nav") or bit_nav.DEFAULT_NAV | |
| # Overlays. At most one is open at a time; the palette wins because ⌘K | |
| # should always be reachable. | |
| overlay = "" | |
| if state.get("palette_open"): | |
| overlay = palette.render( | |
| state.get("palette_q") or "", view.get("palette_groups") or [], emit, | |
| total_hint=f"{index.total:,} indexed", | |
| ) | |
| elif state.get("soon_module"): | |
| item = bit_nav.find(navigation, state["soon_module"]) | |
| overlay = dialogs.coming_soon(item, navigation, emit, | |
| notice=state.get("notify_note", "")) | |
| elif state.get("contact_open"): | |
| overlay = dialogs.contact(navigation, state, emit) | |
| elif selected: | |
| overlay = drawer(selected, index) | |
| return f""" | |
| <div class="bit-atlas bit-app" data-theme="dark"> | |
| {bit_sidebar.render(navigation, active='Finance Atlas', emit=emit)} | |
| <div style="flex:1 1 auto;min-width:0;display:flex;flex-direction:column"> | |
| {header(index, view['tape'])} | |
| <main style="flex:1 1 auto;min-width:0;padding:12px;display:flex; | |
| flex-direction:column;gap:12px"> | |
| {title_row(index, state, matched)} | |
| {stat_band(index)} | |
| <div class="bit-zones" style="display:flex;align-items:flex-start;gap:12px"> | |
| {filter_rail(index, state, view['hidden'])} | |
| {model_table(index, state, view['rows'], view['hidden'], | |
| matched=matched, truncated=truncated)} | |
| {right_rail(index, state, view['trending'])} | |
| </div> | |
| {footer(index, view['links'])} | |
| </main> | |
| </div> | |
| {overlay} | |
| </div>""" | |