Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions apps/playground/src/components/playground/image-page-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,12 @@ export default function ImagePageClient({
}
}, [selectedModels]);

useEffect(() => {
setModelPreferenceCookie(IMAGE_SERVICE_TIER_COOKIE, imageServiceTier);
}, [imageServiceTier]);
// The tier only changes through this handler, so persist the cookie here
// instead of watching the state with an effect.
const handleServiceTierChange = useCallback((tier: ImageServiceTier) => {
setImageServiceTier(tier);
setModelPreferenceCookie(IMAGE_SERVICE_TIER_COOKIE, tier);
}, []);

// Sync URL → state for back/forward navigation
useEffect(() => {
Expand Down Expand Up @@ -952,7 +955,7 @@ export default function ImagePageClient({
imageCount={imageCount}
setImageCount={setImageCount}
serviceTier={imageServiceTier}
setServiceTier={setImageServiceTier}
setServiceTier={handleServiceTierChange}
supportsFlex={supportsFlex}
isGenerating={isGenerating}
onGenerate={generateImages}
Expand Down
12 changes: 7 additions & 5 deletions apps/playground/src/lib/image-download.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { zip } from "fflate";

import type { GalleryImage, GeneratedImage } from "@/lib/image-gen";

export function base64ToBlob(base64: string, mediaType: string): Blob {
Expand Down Expand Up @@ -100,9 +98,13 @@ export function zipEntryName(
// Bundles every image of a gallery item into one archive. Generated images
// are already compressed, so entries are stored rather than deflated.
export async function downloadImagesAsZip(entries: ZipEntry[], stem: string) {
const blobs = await Promise.all(
entries.map((entry) => loadImageBlob(entry.image)),
);
// fflate is only needed for this click-triggered path, so load it on
// demand in parallel with the image bytes instead of shipping it in the
// image page's initial bundle.
const [{ zip }, blobs] = await Promise.all([
import("fflate"),
Promise.all(entries.map((entry) => loadImageBlob(entry.image))),
]);
const files: Record<string, [Uint8Array, { level: 0 }]> = {};
for (let index = 0; index < blobs.length; index++) {
const blob = blobs[index]!;
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/discounted/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function DiscountedModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Discounted Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/embeddings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function EmbeddingModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Embedding Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/image-to-image/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function ImageToImageModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Image-to-Image Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Link from "next/link";
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { JsonLd } from "@/components/seo/json-ld";
Expand Down Expand Up @@ -106,6 +107,7 @@ export default async function ModelsPage() {
<JsonLd data={[collectionSchema, itemListSchema, breadcrumbSchema]} />
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="AI Models Directory"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/reasoning/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function ReasoningModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Reasoning Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/text-to-image/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function TextToImageModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Text-to-Image Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/text/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function TextModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Text Generation Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/tools/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function ToolsModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Tool-Calling Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/video/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function VideoModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Video Generation Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/vision/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function VisionModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Vision Models"
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/app/models/web-search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { fetchModels, fetchProviders } from "@/lib/fetch-models";
Expand All @@ -25,6 +26,7 @@ export default async function WebSearchModelsPage() {
return (
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title="Web Search Models"
Expand Down
45 changes: 4 additions & 41 deletions apps/ui/src/components/compare/hero-compare.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
"use client";
import Link from "next/link";
import React from "react";

import { AnimatedGroup } from "@/components/landing/animated-group";
import { Navbar } from "@/components/landing/navbar";
import { AuthLink } from "@/components/shared/auth-link";
import { Button } from "@/lib/components/button";

import type { Variants } from "@/components/motion-wrapper";
import type { Route } from "next";

const transitionVariants: { item: Variants } = {
item: {
hidden: {
opacity: 0,
filter: "blur(12px)",
y: 12,
},
visible: {
opacity: 1,
filter: "blur(0px)",
y: 0,
transition: {
type: "spring" as const,
bounce: 0.3,
duration: 1.5,
},
},
},
};

interface HeroContent {
heading: string;
description: string;
Expand Down Expand Up @@ -98,7 +74,7 @@ export function HeroCompare({ content }: HeroCompareProps) {
/>
<div className="mx-auto max-w-7xl px-6">
<div className="text-center sm:mx-auto lg:mr-auto lg:mt-0">
<AnimatedGroup variants={transitionVariants}>
<div className="animate-hero-enter">
<h1 className="mt-8 max-w-4xl mx-auto text-balance text-2xl md:text-7xl lg:mt-16 xl:text-[5.25rem]">
{heroContent.heading}
</h1>
Expand All @@ -118,22 +94,9 @@ export function HeroCompare({ content }: HeroCompareProps) {
))}
</div>
)}
</AnimatedGroup>
</div>

<AnimatedGroup
variants={{
container: {
visible: {
transition: {
staggerChildren: 0.05,
delayChildren: 0.75,
},
},
},
...transitionVariants,
}}
className="mt-12 flex flex-col items-center justify-center gap-2 md:flex-row"
>
<div className="animate-hero-enter hero-enter-delay-1 mt-12 flex flex-col items-center justify-center gap-2 md:flex-row">
<div
key={1}
className="bg-foreground/10 rounded-[14px] border p-0.5"
Expand Down Expand Up @@ -190,7 +153,7 @@ export function HeroCompare({ content }: HeroCompareProps) {
</Link>
)}
</Button>
</AnimatedGroup>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
1 change: 0 additions & 1 deletion apps/ui/src/components/landing/comparison-bedrock.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
1 change: 0 additions & 1 deletion apps/ui/src/components/landing/comparison-litellm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
1 change: 0 additions & 1 deletion apps/ui/src/components/landing/comparison-portkey.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
1 change: 0 additions & 1 deletion apps/ui/src/components/landing/comparison-vercel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
1 change: 0 additions & 1 deletion apps/ui/src/components/landing/comparison.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"use client";
import { Check, X } from "lucide-react";
import Link from "next/link";

Expand Down
8 changes: 5 additions & 3 deletions apps/ui/src/components/landing/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
"use client";
import { GithubIcon } from "lucide-react";
import Link from "next/link";

import Newsletter from "@/components/landing/newsletter";
import { useAppConfig } from "@/lib/config";
import { getConfig } from "@/lib/config-server";
import { XIcon } from "@/lib/icons/XIcon";
import { listedProviders } from "@/lib/providers-catalog";

// Server component on purpose: the provider directory below pulls the full
// model catalogue via providers-catalog, which must stay out of the client
// bundle of every marketing page that renders the footer.
export default function Footer() {
const config = useAppConfig();
const config = getConfig();

return (
<footer className="relative py-12 bg-background">
Expand Down
9 changes: 3 additions & 6 deletions apps/ui/src/components/models/all-models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useSearchParams } from "next/navigation";

import Footer from "@/components/landing/footer";
import { ModelCtaButton } from "@/components/models/model-cta-button";

import { AllModels as SharedAllModels } from "@llmgateway/shared/components";
Expand All @@ -11,11 +10,10 @@ import type { ComponentProps } from "react";

type SharedAllModelsProps = ComponentProps<typeof SharedAllModels>;

// `footer` is passed in from the server pages (usually <Footer />) so the
// server-only footer never enters this client bundle.
export function AllModels(
props: Omit<
SharedAllModelsProps,
"footer" | "renderCta" | "showPricingTierFilter"
>,
props: Omit<SharedAllModelsProps, "renderCta" | "showPricingTierFilter">,
) {
const searchParams = useSearchParams();
// The premium/standard tier only matters for DevPass fair-use limits, so
Expand All @@ -27,7 +25,6 @@ export function AllModels(
return (
<SharedAllModels
{...props}
footer={<Footer />}
renderCta={(args) => <ModelCtaButton {...args} />}
showPricingTierFilter={showPricingTierFilter}
/>
Expand Down
2 changes: 2 additions & 0 deletions apps/ui/src/components/models/category-page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Suspense } from "react";

import Footer from "@/components/landing/footer";
import { HeroRSC } from "@/components/landing/hero-rsc";
import { AllModels } from "@/components/models/all-models";
import { CategorySeoContent } from "@/components/models/category-seo-content";
Expand Down Expand Up @@ -107,6 +108,7 @@ export async function ModelCategoryPage({ slug }: { slug: ModelCategorySlug }) {
<JsonLd data={[collectionSchema, breadcrumbSchema, faqSchema]} />
<Suspense>
<AllModels
footer={<Footer />}
models={models}
providers={providers}
title={content.heading}
Expand Down
Loading
Loading