diff --git a/src/App.svelte b/src/App.svelte
index 3b1c2f4..a1dc4a3 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -75,11 +75,14 @@
.then((res) => res.text())
.then((text) => {
lines = text.split("\n");
- const items: Item[] = Object.entries(JSON.parse(text)).map(
- ([k, v]: any) => ({ name: k, ...v }),
- );
-
- providers = [...new Set(items.map((i) => i.litellm_provider))];
+ // Upstream JSON ships a "sample_spec" pseudo-entry that documents
+ // the schema (each field's value is a description string, not data).
+ // It is not a real model — drop it from the table.
+ const items: Item[] = Object.entries(JSON.parse(text))
+ .filter(([k]) => k !== "sample_spec")
+ .map(([k, v]: any) => ({ name: k, ...v }));
+
+ providers = [...new Set(items.map((i) => i.litellm_provider).filter(Boolean))];
providers.sort();
index = new Fuse(items, {
@@ -354,25 +357,25 @@ We also need to update [${RESOURCE_BACKUP_NAME}](https://github.com/${REPO_FULL_
- Netflix
+ Netflix
-
+
Twilio
-
+
Zurich
- Rocket Money
- Lemonade
-
+ Rocket Money
+ Lemonade
+
The Weather Company
- samsara
+ samsara
@@ -817,12 +820,24 @@ curl http://0.0.0.0:4000/v1/chat/completions \
flex-wrap: wrap;
}
+ /* Unified hover transition for all trust logos (img + text + svg-icon).
+ Same duration/easing across the board so each one fades smoothly
+ into its colored state. */
+ .trust-logo-img,
+ .trust-logo-text,
+ .trust-logo-icon svg,
+ .trust-logo-svg {
+ transition: color 0.3s ease-out,
+ fill 0.3s ease-out,
+ opacity 0.3s ease-out,
+ filter 0.3s ease-out;
+ }
+
.trust-logo-img {
height: 28px;
object-fit: contain;
opacity: 0.55;
filter: grayscale(100%);
- transition: all 0.2s ease;
}
.trust-logo-img:hover {
@@ -841,11 +856,47 @@ curl http://0.0.0.0:4000/v1/chat/completions \
}
}
+ /* Text logos use the same grayscale->color treatment as image logos.
+ The brand color is the BASE color; the grayscale filter desaturates
+ it by default, and removing the filter on hover reveals it. */
.trust-logo-text {
font-size: 1.125rem;
font-weight: 700;
- color: var(--border-color-strong);
+ color: var(--text-color);
letter-spacing: 0.04em;
+ opacity: 0.55;
+ filter: grayscale(100%);
+ cursor: default;
+ /* Promote to its own compositing layer so filter transitions don't
+ cause subpixel jitter in the inline SVG + text combo (Zurich,
+ Weather Company, Twilio). */
+ transform: translateZ(0);
+ will-change: filter, opacity;
+ backface-visibility: hidden;
+ }
+
+ .trust-logo-text:hover {
+ opacity: 0.9;
+ filter: grayscale(0%);
+ }
+
+ .trust-logo-text[data-brand="netflix"] { color: #E50914; }
+ .trust-logo-text[data-brand="lemonade"] { color: #FF0083; }
+ .trust-logo-text[data-brand="rocketmoney"] { color: #00D4AA; }
+ .trust-logo-text[data-brand="twilio"] { color: #F22F46; }
+ .trust-logo-text[data-brand="zurich"] { color: #2167AE; }
+ .trust-logo-text[data-brand="weather"] { color: #00ABEB; }
+ .trust-logo-text[data-brand="samsara"] { color: #1F6FB2; }
+
+ @media (prefers-color-scheme: dark) {
+ .trust-logo-text {
+ filter: grayscale(100%) brightness(2);
+ opacity: 0.5;
+ }
+ .trust-logo-text:hover {
+ filter: grayscale(0%) brightness(1.2);
+ opacity: 0.9;
+ }
}
.trust-logo-icon {
@@ -856,26 +907,27 @@ curl http://0.0.0.0:4000/v1/chat/completions \
.trust-logo-icon svg {
flex-shrink: 0;
+ fill: currentColor;
}
.trust-logo-svg {
filter: brightness(0);
- opacity: 0.45;
+ opacity: 0.55;
}
.trust-logo-svg:hover {
filter: brightness(0);
- opacity: 0.8;
+ opacity: 1;
}
@media (prefers-color-scheme: dark) {
.trust-logo-svg {
filter: brightness(0) invert(1);
- opacity: 0.5;
+ opacity: 0.6;
}
.trust-logo-svg:hover {
filter: brightness(0) invert(1);
- opacity: 0.9;
+ opacity: 1;
}
}
@@ -1095,9 +1147,6 @@ curl http://0.0.0.0:4000/v1/chat/completions \
background-color: var(--bg-secondary);
white-space: nowrap;
user-select: none;
- position: sticky;
- top: 63px;
- z-index: 10;
}
.th-model { padding-left: 1rem; }
@@ -1125,6 +1174,7 @@ curl http://0.0.0.0:4000/v1/chat/completions \
border-bottom: 1px solid var(--border-color);
transition: background-color 0.1s ease;
cursor: pointer;
+ height: 52px;
}
tbody tr.model-row:hover {
@@ -1153,6 +1203,9 @@ curl http://0.0.0.0:4000/v1/chat/completions \
display: flex;
align-items: center;
gap: 0.625rem;
+ flex-wrap: nowrap;
+ min-width: 0;
+ min-height: 32px;
}
.expand-icon {
@@ -1224,6 +1277,8 @@ curl http://0.0.0.0:4000/v1/chat/completions \
align-items: center;
gap: 0.5rem;
min-width: 0;
+ flex: 1 1 auto;
+ flex-wrap: nowrap;
}
.model-title {
@@ -1233,6 +1288,8 @@ curl http://0.0.0.0:4000/v1/chat/completions \
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ flex: 1 1 auto;
+ min-width: 0;
}
.mode-badge {
@@ -1246,6 +1303,9 @@ curl http://0.0.0.0:4000/v1/chat/completions \
text-transform: uppercase;
letter-spacing: 0.03em;
flex-shrink: 0;
+ max-width: 110px;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.context-cell {
@@ -1334,6 +1394,8 @@ curl http://0.0.0.0:4000/v1/chat/completions \
align-items: center;
padding: 0.375rem 0;
border-bottom: 1px solid var(--border-color);
+ gap: 0.75rem;
+ min-width: 0;
}
.info-row:last-child { border-bottom: none; }
@@ -1341,6 +1403,7 @@ curl http://0.0.0.0:4000/v1/chat/completions \
.info-label {
font-size: 0.8125rem;
color: var(--muted-color);
+ flex-shrink: 0;
}
.info-value {
@@ -1348,6 +1411,9 @@ curl http://0.0.0.0:4000/v1/chat/completions \
font-weight: 600;
color: var(--text-color);
font-family: 'JetBrains Mono', monospace;
+ text-align: right;
+ overflow-wrap: anywhere;
+ min-width: 0;
}
.feature-list {
@@ -1449,9 +1515,13 @@ curl http://0.0.0.0:4000/v1/chat/completions \
overflow-x: auto;
background: var(--code-bg);
color: var(--code-text);
+ max-width: 100%;
+ box-sizing: border-box;
+ -webkit-overflow-scrolling: touch;
}
- .code-snippet code { display: block; }
+ .code-snippet code { display: block; white-space: pre; }
+ .detail-code-section { max-width: 100%; min-width: 0; }
.code-kw { color: #8b5cf6; }
.code-str { color: #10b981; }
@@ -1518,6 +1588,12 @@ curl http://0.0.0.0:4000/v1/chat/completions \
.th-hide-mobile, .td-hide-mobile { display: none; }
}
+ /* Tablet — intermediate code-snippet size between desktop and phone */
+ @media (max-width: 1024px) {
+ .code-snippet { font-size: 0.6875rem; line-height: 1.5; padding: 0.75rem; }
+ .code-snippet code { white-space: pre-wrap; word-break: break-word; }
+ }
+
@media (max-width: 768px) {
.hero { padding: 2.5rem 1rem 1.5rem; }
.hero-title { font-size: 2rem; }
@@ -1532,6 +1608,36 @@ curl http://0.0.0.0:4000/v1/chat/completions \
.model-name { min-width: 180px; }
.trust-logos { gap: 1.25rem; }
.trust-logo-img { height: 22px; }
- .detail-grid { grid-template-columns: 1fr; }
+ .trust-logo-text { font-size: 0.9375rem; }
+ .detail-grid { grid-template-columns: 1fr; gap: 1rem; }
+
+ /* Detail panel: shrink section headings and pricing values so they
+ no longer dominate the body text on phones. Section headings
+ (Pricing, Model Info, Features) sit one tier above pricing-card
+ labels (Input, Cache Read, ...) for clear hierarchy. */
+ .detail-panel { padding: 1rem; }
+ .detail-heading { font-size: 0.5625rem; letter-spacing: 0.06em; margin-bottom: 0.5rem; }
+ .pricing-cards { gap: 0.3125rem; }
+ .pricing-card { padding: 0.375rem 0.4375rem; gap: 0.125rem; }
+ .pricing-label { font-size: 0.375rem; letter-spacing: 0.04em; font-weight: 500; }
+ .pricing-value { font-size: 0.5625rem; font-weight: 600; }
+
+ /* Stack info-rows so "Max Input" label can't collide with long values */
+ .info-row { flex-direction: column; align-items: flex-start; gap: 0.125rem; padding: 0.375rem 0; }
+ .info-label { font-size: 0.75rem; }
+ .info-value { text-align: left; font-size: 0.75rem; word-break: break-word; }
+
+ /* Code blocks: smaller font and tighter padding for mobile readability.
+ Wrap lines (pre-wrap) so very long model names break instead of
+ forcing the block to be visually huge. */
+ .code-snippet { font-size: 0.4375rem; line-height: 1.4; padding: 0.5rem; }
+ .code-snippet code { white-space: pre-wrap; word-break: break-word; }
+ .code-header-row { padding: 0.5rem 0.75rem; flex-wrap: wrap; gap: 0.5rem; }
+ .code-tab { padding: 0.3125rem 0.625rem; font-size: 0.6875rem; }
+ .copy-code-btn { font-size: 0.6875rem; padding: 0.25rem 0.5rem; }
+
+ /* Lock row height so Bedrock / first entries don't render taller than others */
+ .model-row td { height: 48px; }
+ .provider-avatar { width: 24px; height: 24px; }
}
diff --git a/src/Providers.svelte b/src/Providers.svelte
index fb90b93..75d604d 100644
--- a/src/Providers.svelte
+++ b/src/Providers.svelte
@@ -32,20 +32,26 @@
const PROVIDERS_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/provider_endpoints_support.json";
const DOCS_URL = "https://docs.litellm.ai/docs/providers";
+ // Upstream JSON ships a few broken /docs/providers/* URLs that 404.
+ // Map provider key -> correct doc URL.
+ const PROVIDER_URL_OVERRIDES: Record = {
+ a2a: "https://docs.litellm.ai/docs/a2a",
+ };
+
onMount(async () => {
try {
const response = await fetch(PROVIDERS_URL);
const data = await response.json();
-
+
if (data.endpoints) {
endpointsMetadata = data.endpoints;
}
-
+
if (data.providers) {
providers = Object.entries(data.providers).map(([provider, info]: [string, any]) => ({
provider,
display_name: info.display_name || provider,
- url: info.url || DOCS_URL,
+ url: PROVIDER_URL_OVERRIDES[provider] || info.url || DOCS_URL,
endpoints: info.endpoints || {}
}));
}