chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:28:55 +08:00
commit db42b91b75
6397 changed files with 146012 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<title>__PAGE_TITLE__</title>
<meta
name="description"
content="__PAGE_DESCRIPTION__"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<base href="/" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin="anonymous"
/>
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&amp;family=Rubik:wght@300..900&amp;display=swap"
rel="stylesheet"
/>
<link
rel="icon"
href="./public/favicon.svg"
sizes="any"
type="image/svg+xml"
/>
<meta property="og:title" content="__PAGE_TITLE__" />
<meta property="og:description" content="__PAGE_DESCRIPTION__" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://models.dev/social-share.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="__PAGE_TITLE__" />
<meta name="twitter:description" content="__PAGE_DESCRIPTION__" />
<meta charset="UTF-8" />
<link rel="stylesheet" href="./src/index.css" />
</head>
<body>
<!--static-->
<script src="./src/index.ts" type="module"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@models.dev/web",
"scripts": {
"dev": "bun run --hot ./src/server.ts",
"build": "./script/build.ts"
},
"dependencies": {
"@tanstack/virtual-core": "^3.14.0",
"hono": "^4.8.0",
"@models.dev/core": "workspace:*"
},
"devDependencies": {
"@types/bun": "^1.2.16"
}
}
+2
View File
@@ -0,0 +1,2 @@
/*
Access-Control-Allow-Origin: *
+5
View File
@@ -0,0 +1,5 @@
<svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="600" height="600" fill="black"/>
<path d="M176.04 397C174.162 397 172.473 396.347 170.971 395.04C169.657 393.733 169 392.053 169 390V208C169 205.947 169.657 204.267 170.971 202.96C172.473 201.653 174.162 201 176.04 201H202.228C205.044 201 207.109 201.747 208.423 203.24C209.924 204.733 210.957 205.853 211.52 206.6L263.333 301.52L315.709 206.6C316.084 205.853 316.929 204.733 318.243 203.24C319.745 201.747 321.904 201 324.719 201H350.907C352.972 201 354.662 201.653 355.976 202.96C357.29 204.267 357.947 205.947 357.947 208V390C357.947 392.053 357.29 393.733 355.976 395.04C354.662 396.347 352.972 397 350.907 397H322.185C320.308 397 318.712 396.347 317.398 395.04C316.084 393.733 315.427 392.053 315.427 390V276.88L279.665 343.52C278.726 345.2 277.506 346.693 276.004 348C274.503 349.307 272.531 349.96 270.091 349.96H256.856C254.416 349.96 252.445 349.307 250.943 348C249.441 346.693 248.221 345.2 247.282 343.52L211.52 276.88V390C211.52 392.053 210.863 393.733 209.549 395.04C208.235 396.347 206.639 397 204.762 397H176.04Z" fill="white"/>
<path d="M392.422 397C390.357 397 388.668 396.347 387.354 395.04C386.039 393.733 385.382 392.053 385.382 390V358.64C385.382 356.587 386.039 354.907 387.354 353.6C388.668 352.293 390.357 351.64 392.422 351.64H423.96C426.025 351.64 427.715 352.293 429.029 353.6C430.343 354.907 431 356.587 431 358.64V390C431 392.053 430.343 393.733 429.029 395.04C427.715 396.347 426.025 397 423.96 397H392.422Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bun
import { RenderedPages, Providers, Models, renderDocument } from "../src/render";
import fs from "fs/promises";
import path from "path";
await fs.rm("./dist", { recursive: true, force: true });
await Bun.build({
entrypoints: ["./index.html"],
outdir: "dist",
target: "bun",
});
for await (const file of new Bun.Glob("./public/*").scan()) {
await Bun.write(file.replace("./public/", "./dist/"), Bun.file(file));
}
// Copy provider logos to dist/logos/
await fs.mkdir("./dist/logos", { recursive: true });
// First, copy the default logo
const defaultLogoPath = "../../providers/logo.svg";
const defaultLogo = Bun.file(defaultLogoPath);
if (await defaultLogo.exists()) {
await Bun.write("./dist/logos/default.svg", defaultLogo);
}
// Then copy provider-specific logos
const providersDir = "../../providers";
const entries = await fs.readdir(providersDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const provider = entry.name;
const logoPath = path.join(providersDir, provider, "logo.svg");
const logoFile = Bun.file(logoPath);
if (await logoFile.exists()) {
await Bun.write(`./dist/logos/${provider}.svg`, logoFile);
}
}
}
// Copy lab logos to dist/logos/labs/
await fs.mkdir("./dist/logos/labs", { recursive: true });
const labsDir = "../../labs";
try {
const labEntries = await fs.readdir(labsDir, { withFileTypes: true });
for (const entry of labEntries) {
if (entry.isDirectory()) {
const lab = entry.name;
const logoPath = path.join(labsDir, lab, "logo.svg");
const logoFile = Bun.file(logoPath);
if (await logoFile.exists()) {
await Bun.write(`./dist/logos/labs/${lab}.svg`, logoFile);
}
}
}
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
throw error;
}
}
const template = await Bun.file("./dist/index.html").text();
for (const [route, rendered] of RenderedPages) {
const filePath = route === "/"
? "./dist/_index.html"
: path.join("./dist", route, "index.html");
await fs.mkdir(path.dirname(filePath), { recursive: true });
await Bun.write(filePath, renderDocument(template, rendered));
}
await Bun.write("./dist/api.json", JSON.stringify(Providers));
await Bun.write(
"./dist/catalog.json",
JSON.stringify({ models: Models, providers: Providers }),
);
await Bun.write("./dist/models.json", JSON.stringify(Models));
await fs.rename("./dist/api.json", "./dist/_api.json");
await fs.rename("./dist/catalog.json", "./dist/_catalog.json");
await fs.rename("./dist/models.json", "./dist/_models.json");
await fs.rm("./dist/index.html", { force: true });
File diff suppressed because it is too large Load Diff
+763
View File
@@ -0,0 +1,763 @@
type SortDirection = "asc" | "desc";
interface SearchIndexItem {
type: "model" | "provider" | "lab";
title: string;
id: string;
href: string;
logo: string;
tokens: string[];
lab?: string;
modelCount?: number;
providerCount?: number;
context?: number;
releaseDate?: string;
inputCost?: number;
outputCost?: number;
description?: string;
npm?: string;
api?: string;
updated?: string;
}
interface SearchResult {
item: SearchIndexItem;
score: number;
}
const helpModal = document.getElementById("modal") as HTMLDialogElement | null;
const modalClose = document.getElementById("close");
const help = document.getElementById("help");
const mobileMenu = document.getElementById(
"mobile-menu",
) as HTMLDialogElement | null;
const mobileMenuTrigger = document.getElementById("mobile-menu-trigger");
const mobileMenuClose = document.getElementById("mobile-menu-close");
const mobileSearchTrigger = document.getElementById("mobile-search-trigger");
const mobileHelpTrigger = document.getElementById("mobile-help-trigger");
const searchModal = document.getElementById(
"search-modal",
) as HTMLDialogElement | null;
const searchTrigger = document.getElementById("search-trigger");
const searchInput = document.getElementById(
"search-input",
) as HTMLInputElement | null;
const searchResults = document.getElementById("search-results");
const searchCount = document.getElementById("search-count");
const searchEmpty = document.getElementById("search-empty");
const tables = Array.from(
document.querySelectorAll<HTMLTableElement>("table[data-enhanced-table]"),
);
let scrollYBeforeModal = 0;
let lastFocusedElement: HTMLElement | null = null;
let activeSearchIndex = 0;
let rankedSearchResults: SearchResult[] = [];
const searchItems = parseSearchIndex();
const compactNumberFormatter = new Intl.NumberFormat(undefined, {
notation: "compact",
maximumFractionDigits: 1,
});
/////////////////////////
// Help Dialog
/////////////////////////
function openHelpDialog() {
if (!helpModal) return;
if (searchModal?.open) closeSearchModal();
if (mobileMenu?.open) closeMobileMenu(false);
scrollYBeforeModal = window.scrollY;
document.body.style.position = "fixed";
document.body.style.top = `-${scrollYBeforeModal}px`;
helpModal.showModal();
}
help?.addEventListener("click", openHelpDialog);
function closeDialog() {
if (!helpModal) return;
helpModal.close();
document.body.style.position = "";
document.body.style.top = "";
window.scrollTo(0, scrollYBeforeModal);
}
modalClose?.addEventListener("click", closeDialog);
helpModal?.addEventListener("cancel", closeDialog);
helpModal?.addEventListener("click", (event) => {
if (event.target === helpModal) closeDialog();
});
////////////////////
// Search
////////////////////
function parseSearchIndex() {
const index = document.getElementById("search-index")?.textContent;
if (!index) return [];
try {
const parsed = JSON.parse(index);
return Array.isArray(parsed) ? (parsed as SearchIndexItem[]) : [];
} catch {
return [];
}
}
function normalizeSearchText(value: string) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
}
function searchFields(item: SearchIndexItem) {
return [item.title, item.id, ...item.tokens].filter(Boolean);
}
function fuzzySequenceScore(haystack: string, needle: string) {
let score = 0;
let previousIndex = -1;
let searchFrom = 0;
for (const character of needle) {
const index = haystack.indexOf(character, searchFrom);
if (index === -1) return 0;
if (index === 0 || haystack[index - 1] === " ") {
score += 8;
} else if (index === previousIndex + 1) {
score += 6;
} else {
score += 2;
}
previousIndex = index;
searchFrom = index + 1;
}
return score + Math.max(0, 12 - haystack.length / 8);
}
function scoreTerm(field: string, term: string) {
const normalized = normalizeSearchText(field);
if (!normalized) return 0;
if (normalized === term) return 120;
if (normalized.startsWith(term)) return 100;
if (normalized.split(" ").some((word) => word.startsWith(term))) return 82;
const index = normalized.indexOf(term);
if (index !== -1) return 64 - Math.min(index, 24);
return fuzzySequenceScore(normalized, term);
}
function scoreSearchItem(item: SearchIndexItem, query: string) {
const terms = normalizeSearchText(query).split(/\s+/).filter(Boolean);
if (terms.length === 0) return 1;
let score = 0;
for (const term of terms) {
let best = 0;
for (const field of searchFields(item)) {
best = Math.max(best, scoreTerm(field, term));
}
if (best <= 0) return 0;
score += best;
}
const normalizedTitle = normalizeSearchText(item.title);
const normalizedId = normalizeSearchText(item.id);
const normalizedQuery = normalizeSearchText(query);
if (normalizedTitle === normalizedQuery || normalizedId === normalizedQuery) {
score += 120;
} else if (normalizedTitle.startsWith(normalizedQuery)) {
score += 44;
} else if (normalizedId.startsWith(normalizedQuery)) {
score += 36;
}
if (item.type === "model") score += 8;
return score;
}
function rankSearchItems(query: string) {
const normalizedQuery = normalizeSearchText(query);
const results = searchItems
.map((item) => ({ item, score: scoreSearchItem(item, normalizedQuery) }))
.filter((result) => result.score > 0)
.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
const dateComparison = compareSearchDates(
searchSortDate(a.item),
searchSortDate(b.item),
);
if (dateComparison !== 0) return dateComparison;
return a.item.title.localeCompare(b.item.title, undefined, {
numeric: true,
sensitivity: "base",
});
});
return normalizedQuery ? results.slice(0, 40) : results.slice(0, 18);
}
function compareSearchDates(a?: string, b?: string) {
if (a === undefined && b === undefined) return 0;
if (a === undefined) return 1;
if (b === undefined) return -1;
return b.localeCompare(a);
}
function searchSortDate(item: SearchIndexItem) {
return item.releaseDate ?? item.updated;
}
function formatCompactNumber(value?: number) {
if (value === undefined) return undefined;
return compactNumberFormatter.format(value);
}
function formatCost(input?: number, output?: number) {
if (input === undefined && output === undefined) return undefined;
const inputText = input === undefined ? "-" : `$${input.toFixed(2)}`;
const outputText = output === undefined ? "-" : `$${output.toFixed(2)}`;
return `${inputText} / ${outputText}`;
}
function appendHighlightedText(
element: HTMLElement,
text: string,
query: string,
) {
const terms = normalizeSearchText(query).split(/\s+/).filter(Boolean);
if (terms.length === 0) {
element.textContent = text;
return;
}
const lowerText = text.toLowerCase();
const ranges = terms
.map((term) => {
const index = lowerText.indexOf(term);
return index === -1 ? undefined : [index, index + term.length] as const;
})
.filter((range): range is readonly [number, number] => range !== undefined)
.sort((a, b) => a[0] - b[0]);
if (ranges.length === 0) {
element.textContent = text;
return;
}
let cursor = 0;
for (const [start, end] of ranges) {
if (start < cursor) continue;
if (start > cursor) {
element.append(document.createTextNode(text.slice(cursor, start)));
}
const mark = document.createElement("mark");
mark.textContent = text.slice(start, end);
element.append(mark);
cursor = end;
}
if (cursor < text.length) {
element.append(document.createTextNode(text.slice(cursor)));
}
}
function resultMeta(item: SearchIndexItem) {
if (item.type === "model") {
return [
item.lab,
item.providerCount === undefined
? undefined
: `${item.providerCount} providers`,
item.context === undefined
? undefined
: `${formatCompactNumber(item.context)} context`,
formatCost(item.inputCost, item.outputCost),
item.updated,
].filter((value): value is string => Boolean(value));
}
if (item.type === "provider") {
return [
item.modelCount === undefined ? undefined : `${item.modelCount} models`,
item.npm,
item.api,
].filter((value): value is string => Boolean(value));
}
return [
item.modelCount === undefined ? undefined : `${item.modelCount} models`,
item.providerCount === undefined
? undefined
: `${item.providerCount} providers`,
item.updated,
].filter((value): value is string => Boolean(value));
}
function resultSubtitle(item: SearchIndexItem) {
if (item.type === "model") return item.id;
if (item.type === "provider") return item.id;
return item.id;
}
function createSearchResult(result: SearchResult, index: number, query: string) {
const { item } = result;
const link = document.createElement("a");
link.className = `search-result search-result-${item.type}`;
link.href = item.href;
link.id = `search-result-${index}`;
link.setAttribute("role", "option");
link.setAttribute("aria-selected", index === activeSearchIndex ? "true" : "false");
link.dataset.searchIndex = String(index);
if (index === activeSearchIndex) link.classList.add("is-active");
const icon = document.createElement("span");
icon.className = "search-result-icon";
const logo = document.createElement("img");
logo.src = item.logo;
logo.alt = "";
logo.loading = "lazy";
icon.append(logo);
link.append(icon);
const body = document.createElement("span");
body.className = "search-result-body";
const top = document.createElement("span");
top.className = "search-result-top";
const title = document.createElement("span");
title.className = "search-result-title";
appendHighlightedText(title, item.title, query);
top.append(title);
const kind = document.createElement("span");
kind.className = "search-result-kind";
kind.textContent = item.type;
top.append(kind);
body.append(top);
const subtitle = document.createElement("span");
subtitle.className = "search-result-subtitle mono";
appendHighlightedText(subtitle, resultSubtitle(item), query);
body.append(subtitle);
const meta = document.createElement("span");
meta.className = "search-result-meta";
for (const value of resultMeta(item)) {
const chip = document.createElement("span");
chip.textContent = value;
meta.append(chip);
}
body.append(meta);
link.append(body);
return link;
}
function updateActiveSearchResult() {
if (!searchResults || !searchInput) return;
const resultNodes = Array.from(
searchResults.querySelectorAll<HTMLElement>(".search-result"),
);
for (const [index, result] of resultNodes.entries()) {
const active = index === activeSearchIndex;
result.classList.toggle("is-active", active);
result.setAttribute("aria-selected", active ? "true" : "false");
if (active) {
searchInput.setAttribute("aria-activedescendant", result.id);
result.scrollIntoView({ block: "nearest" });
}
}
}
function setActiveSearchIndex(index: number) {
if (rankedSearchResults.length === 0) return;
activeSearchIndex =
(index + rankedSearchResults.length) % rankedSearchResults.length;
updateActiveSearchResult();
}
function renderSearchResults() {
if (!searchInput || !searchResults || !searchCount || !searchEmpty) return;
const query = searchInput.value;
rankedSearchResults = rankSearchItems(query);
activeSearchIndex = rankedSearchResults.length > 0 ? 0 : -1;
searchResults.replaceChildren();
const fragment = document.createDocumentFragment();
rankedSearchResults.forEach((result, index) => {
fragment.append(createSearchResult(result, index, query));
});
searchResults.append(fragment);
const normalizedQuery = normalizeSearchText(query);
searchCount.textContent = normalizedQuery
? `${rankedSearchResults.length} result${rankedSearchResults.length === 1 ? "" : "s"}`
: "Recently updated models, providers, and labs";
searchEmpty.hidden = rankedSearchResults.length > 0;
if (rankedSearchResults.length > 0) {
searchInput.setAttribute("aria-activedescendant", "search-result-0");
} else {
searchInput.removeAttribute("aria-activedescendant");
}
}
function openSearchModal() {
if (!searchModal || !searchInput) return;
if (helpModal?.open) closeDialog();
if (mobileMenu?.open) closeMobileMenu(false);
lastFocusedElement =
document.activeElement instanceof HTMLElement
? document.activeElement
: null;
if (!searchModal.open) searchModal.showModal();
renderSearchResults();
requestAnimationFrame(() => {
searchInput.focus();
searchInput.select();
});
}
function closeSearchModal() {
if (!searchModal) return;
if (searchModal.open) searchModal.close();
searchInput?.removeAttribute("aria-activedescendant");
lastFocusedElement?.focus();
}
function closestSearchResult(target: EventTarget | null) {
if (!(target instanceof Element)) return null;
return target.closest<HTMLElement>(".search-result[data-search-index]");
}
searchTrigger?.addEventListener("click", openSearchModal);
mobileSearchTrigger?.addEventListener("click", openSearchModal);
/////////////////////
// Mobile Menu
/////////////////////
function openMobileMenu() {
if (!mobileMenu || !mobileMenuTrigger) return;
if (searchModal?.open) closeSearchModal();
if (helpModal?.open) closeDialog();
mobileMenu.showModal();
mobileMenuTrigger.setAttribute("aria-expanded", "true");
requestAnimationFrame(() => {
mobileMenu
.querySelector<HTMLElement>(".mobile-menu-list a, .mobile-menu-list button")
?.focus();
});
}
function closeMobileMenu(restoreFocus = true) {
if (!mobileMenu || !mobileMenuTrigger) return;
if (mobileMenu.open) mobileMenu.close();
mobileMenuTrigger.setAttribute("aria-expanded", "false");
if (restoreFocus) mobileMenuTrigger.focus();
}
mobileMenuTrigger?.addEventListener("click", openMobileMenu);
mobileMenuClose?.addEventListener("click", () => closeMobileMenu());
mobileHelpTrigger?.addEventListener("click", openHelpDialog);
mobileMenu?.addEventListener("cancel", (event) => {
event.preventDefault();
closeMobileMenu();
});
mobileMenu?.addEventListener("click", (event) => {
if (event.target === mobileMenu) closeMobileMenu();
});
searchModal?.addEventListener("cancel", (event) => {
event.preventDefault();
closeSearchModal();
});
searchModal?.addEventListener("click", (event) => {
if (event.target === searchModal) closeSearchModal();
});
searchResults?.addEventListener("mousemove", (event) => {
const result = closestSearchResult(event.target);
if (!result?.dataset.searchIndex) return;
setActiveSearchIndex(Number(result.dataset.searchIndex));
});
searchResults?.addEventListener("click", (event) => {
if (closestSearchResult(event.target)) {
searchInput?.removeAttribute("aria-activedescendant");
}
});
searchInput?.addEventListener("input", renderSearchResults);
searchInput?.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
event.preventDefault();
closeSearchModal();
return;
}
if (event.key === "ArrowDown") {
event.preventDefault();
setActiveSearchIndex(activeSearchIndex + 1);
return;
}
if (event.key === "ArrowUp") {
event.preventDefault();
setActiveSearchIndex(activeSearchIndex - 1);
return;
}
if (event.key === "Enter") {
const result = rankedSearchResults[activeSearchIndex];
if (!result) return;
event.preventDefault();
window.location.href = result.item.href;
}
});
document.addEventListener("keydown", (event) => {
const key = event.key.toLowerCase();
if ((event.metaKey || event.ctrlKey) && (key === "k" || key === "f")) {
event.preventDefault();
openSearchModal();
}
});
////////////////////
// Sorting
////////////////////
function getCellSortValue(row: HTMLTableRowElement, index: number) {
const cell = row.cells[index];
return cell?.getAttribute("data-sort") ?? cell?.textContent?.trim() ?? "";
}
function compareValues(a: string, b: string, type: string | null) {
if (a === "" && b === "") return 0;
if (a === "") return 1;
if (b === "") return -1;
if (type === "number") {
return Number(a) - Number(b);
}
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: "base",
});
}
function sortTable(
table: HTMLTableElement,
column: number,
direction: SortDirection,
) {
const tbody = table.tBodies[0];
const header = table.tHead?.rows[0]?.cells[column];
if (!tbody || !header) return;
const type = header.getAttribute("data-type");
const rows = Array.from(tbody.rows).filter(
(row) => !row.classList.contains("empty-row"),
);
rows.sort((rowA, rowB) => {
const comparison = compareValues(
getCellSortValue(rowA, column),
getCellSortValue(rowB, column),
type,
);
return direction === "asc" ? comparison : -comparison;
});
for (const row of rows) {
tbody.appendChild(row);
}
for (const sortable of table.querySelectorAll("th.sortable")) {
sortable.removeAttribute("aria-sort");
const indicator = sortable.querySelector(".sort-indicator");
if (indicator) indicator.textContent = "";
}
header.setAttribute(
"aria-sort",
direction === "asc" ? "ascending" : "descending",
);
const indicator = header.querySelector(".sort-indicator");
if (indicator) indicator.textContent = direction === "asc" ? "↑" : "↓";
}
for (const table of tables) {
const headers = Array.from(table.querySelectorAll<HTMLTableCellElement>("th"));
headers.forEach((header, column) => {
if (!header.classList.contains("sortable")) return;
header.addEventListener("click", () => {
const current = header.getAttribute("aria-sort");
const direction: SortDirection =
current === "ascending" ? "desc" : "asc";
sortTable(table, column, direction);
});
});
}
////////////////////
// Copy Buttons
////////////////////
const copyTimers = new WeakMap<
HTMLButtonElement,
ReturnType<typeof setTimeout>
>();
const pointerCopyTimes = new WeakMap<HTMLButtonElement, number>();
function writeClipboardWithSelection(value: string) {
let copied = false;
const onCopy = (event: ClipboardEvent) => {
event.clipboardData?.setData("text/plain", value);
event.preventDefault();
copied = true;
};
const textarea = document.createElement("textarea");
textarea.value = value;
textarea.setAttribute("readonly", "");
textarea.style.position = "fixed";
textarea.style.top = "0";
textarea.style.left = "0";
textarea.style.width = "1px";
textarea.style.height = "1px";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
window.focus();
textarea.focus();
textarea.select();
textarea.setSelectionRange(0, value.length);
document.addEventListener("copy", onCopy);
try {
return document.execCommand("copy") || copied;
} finally {
document.removeEventListener("copy", onCopy);
textarea.remove();
}
}
async function writeClipboard(value: string) {
if (writeClipboardWithSelection(value)) return true;
if (navigator.clipboard?.writeText) {
try {
await navigator.clipboard.writeText(value);
return true;
} catch {
return false;
}
}
return false;
}
function selectCopySource(button: HTMLButtonElement) {
const source = button
.closest(".code-line, td")
?.querySelector<HTMLElement>("code, .copy-source, span");
const selection = window.getSelection();
if (!source || !selection) return false;
const range = document.createRange();
range.selectNodeContents(source);
selection.removeAllRanges();
selection.addRange(range);
return true;
}
async function copyValue(button: HTMLButtonElement, value: string) {
const originalLabel =
button.dataset.copyLabel ??
button.getAttribute("aria-label") ??
button.title ??
"Copy";
button.dataset.copyLabel = originalLabel;
const copyIcon = button.querySelector<HTMLElement>(".copy-icon");
const checkIcon = button.querySelector<HTMLElement>(".check-icon");
const copied = await writeClipboard(value);
const selected = copied ? false : selectCopySource(button);
window.clearTimeout(copyTimers.get(button));
button.classList.toggle("copied", copied);
button.classList.toggle("selected", selected);
button.classList.toggle("copy-failed", !copied && !selected);
const feedback = copied ? "Copied" : selected ? "Selected" : "Copy failed";
button.setAttribute("aria-label", feedback);
button.title = feedback;
if (copyIcon && checkIcon) {
copyIcon.style.display = copied ? "none" : "block";
checkIcon.style.display = copied ? "block" : "none";
}
copyTimers.set(
button,
setTimeout(() => {
button.classList.remove("copied", "selected", "copy-failed");
button.setAttribute("aria-label", originalLabel);
button.title = originalLabel;
if (copyIcon && checkIcon) {
copyIcon.style.display = "block";
checkIcon.style.display = "none";
}
}, 1200),
);
}
function copyFromEventTarget(target: EventTarget | null) {
if (!(target instanceof Element)) return undefined;
const button = target.closest<HTMLButtonElement>(
".copy-button[data-copy-value]",
);
const value = button?.dataset.copyValue;
if (!button || !value) return undefined;
return { button, value };
}
document.addEventListener("pointerdown", (event) => {
const copy = copyFromEventTarget(event.target);
if (!copy) return;
pointerCopyTimes.set(copy.button, Date.now());
void copyValue(copy.button, copy.value);
});
document.addEventListener("click", (event) => {
const copy = copyFromEventTarget(event.target);
if (!copy) return;
const pointerCopyTime = pointerCopyTimes.get(copy.button);
if (pointerCopyTime && Date.now() - pointerCopyTime < 500) return;
void copyValue(copy.button, copy.value);
});
document.addEventListener("keydown", (event) => {
if (event.key !== "Enter" && event.key !== " ") return;
if (!(event.target instanceof Element)) return;
const copy = copyFromEventTarget(event.target);
if (!copy) return;
event.preventDefault();
void copyValue(copy.button, copy.value);
});
File diff suppressed because it is too large Load Diff
+163
View File
@@ -0,0 +1,163 @@
import Index from "../index.html";
import { getRenderedPage, Models, Providers, renderDocument } from "./render";
import path from "path";
const assetPort = Number(Bun.env.ASSET_PORT ?? 16000);
Bun.serve({
port: assetPort,
routes: {
"/": Index,
"/src/*": (req) => {
const url = new URL(req.url);
const file = Bun.file(
path.join(import.meta.dir, "..", url.pathname.slice(1)),
);
return new Response(file);
},
"/favicon.svg": () =>
new Response(Bun.file(path.join(import.meta.dir, "..", "public/favicon.svg")), {
headers: {
"Content-Type": "image/svg+xml",
},
}),
"/social-share.png": () =>
new Response(Bun.file(path.join(import.meta.dir, "..", "public/social-share.png")), {
headers: {
"Content-Type": "image/png",
},
}),
"/assets/*": (req) => {
const file = Bun.file(
path.join(import.meta.dir, new URL(req.url).pathname)
);
return new Response(file);
},
"/logos/labs/*": async (req) => {
const url = new URL(req.url);
const lab = url.pathname.split("/")[3].replace(".svg", "");
const logoPath = path.join(
import.meta.dir,
"..",
"..",
"..",
"labs",
lab,
"logo.svg"
);
const defaultLogoPath = path.join(
import.meta.dir,
"..",
"..",
"..",
"providers",
"logo.svg"
);
let file = Bun.file(logoPath);
if (!(await file.exists())) {
file = Bun.file(defaultLogoPath);
}
return new Response(file, {
headers: {
"Content-Type": "image/svg+xml",
"Cache-Control": "public, max-age=3600",
},
});
},
"/logos/*": async (req) => {
const url = new URL(req.url);
const provider = url.pathname.split("/")[2].replace(".svg", "");
const logoPath = path.join(
import.meta.dir,
"..",
"..",
"..",
"providers",
provider,
"logo.svg"
);
const defaultLogoPath = path.join(
import.meta.dir,
"..",
"..",
"..",
"providers",
"logo.svg"
);
let file = Bun.file(logoPath);
if (!(await file.exists())) {
file = Bun.file(defaultLogoPath);
}
return new Response(file, {
headers: {
"Content-Type": "image/svg+xml",
"Cache-Control": "public, max-age=3600",
},
});
},
"/api.json": () =>
Response.json(Providers, {
headers: {
"Cache-Control": "public, max-age=3600",
},
}),
"/models.json": () =>
Response.json(Models, {
headers: {
"Cache-Control": "public, max-age=3600",
},
}),
"/catalog.json": () =>
Response.json(
{ models: Models, providers: Providers },
{
headers: {
"Cache-Control": "public, max-age=3600",
},
},
),
},
});
const server = Bun.serve({
development: true,
hostname: "0.0.0.0",
port: Number(Bun.env.PORT ?? 3000),
async fetch(req) {
// Reject WebSocket upgrade requests
if (req.headers.get("upgrade") === "websocket") {
return new Response("WebSocket upgrades not supported", {
status: 426,
headers: {
Upgrade: "Required",
},
});
}
const url = new URL(req.url);
const rendered = getRenderedPage(url.pathname);
if (rendered !== undefined) {
const shellUrl = new URL(url);
shellUrl.host = `localhost:${assetPort}`;
shellUrl.pathname = "/";
shellUrl.search = "";
let html = await fetch(shellUrl.toString(), req).then((r) => r.text());
html = renderDocument(html, rendered);
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
});
}
url.host = `localhost:${assetPort}`;
return fetch(url.toString(), req);
},
});
console.log(`Server running at ${server.hostname}:${server.port}`);
+92
View File
@@ -0,0 +1,92 @@
export interface TableLink {
label: string;
url: string;
title?: string;
}
const MODALITY_ICONS: Record<string, string> = {
text: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4,7 4,4 20,4 20,7"></polyline><line x1="9" y1="20" x2="15" y2="20"></line><line x1="12" y1="4" x2="12" y2="20"></line></svg>`,
image: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect><circle cx="9" cy="9" r="2"></circle><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path></svg>`,
audio: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="m19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg>`,
video: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 8-6 4 6 4V8Z"></path><rect width="14" height="12" x="2" y="6" rx="2" ry="2"></rect></svg>`,
pdf: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14,2 14,8 20,8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10,9 9,9 8,9"></polyline></svg>`,
};
export function escapeHtml(value: string | number) {
return String(value).replace(/[&<>'"]/g, (char) => {
switch (char) {
case "&":
return "&amp;";
case "<":
return "&lt;";
case ">":
return "&gt;";
case "'":
return "&#39;";
case '"':
return "&quot;";
default:
return char;
}
});
}
export function booleanText(value?: boolean) {
if (value === undefined) return "-";
return value ? "Yes" : "No";
}
export function formatCost(cost?: number) {
return cost === undefined ? "-" : `$${cost.toFixed(2)}`;
}
export function formatNumber(value?: number) {
return value === undefined ? "-" : value.toLocaleString();
}
export function knowledgeText(value?: string) {
return value ? value.substring(0, 7) : "-";
}
export function weightsText(value?: boolean) {
if (value === undefined) return "-";
return value ? "Open" : "Closed";
}
export function renderModalityIcon(modality: string) {
const label =
modality === "pdf"
? "PDF"
: modality[0]!.toUpperCase() + modality.slice(1);
const icon = MODALITY_ICONS[modality];
if (!icon) return "";
return `<span class="modality-icon" data-tooltip="${label}">${icon}</span>`;
}
export function renderModalities(modalities?: string[]) {
if (!modalities || modalities.length === 0) return "-";
return `<div class="modalities">${modalities
.map(renderModalityIcon)
.join("")}</div>`;
}
export function costSummary(input?: number, output?: number) {
if (input === undefined && output === undefined) return "-";
return `${formatCost(input)} / ${formatCost(output)}`;
}
export function capabilitySummary(capabilities: Array<[string, boolean | undefined]>) {
const active = capabilities
.filter(([, value]) => value === true)
.map(([label]) => label);
return active.length > 0 ? active.join(", ") : "-";
}
export function sortDate(value?: string) {
return value ?? "";
}
export function sortNumber(value?: number) {
return value === undefined ? "" : String(value);
}
+9
View File
@@ -0,0 +1,9 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"jsx": "react-jsx",
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": [
"ES2020",
"DOM",
"DOM.Iterable"
],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}