File size: 7,850 Bytes
4fb0ce9 95ecc67 4fb0ce9 95ecc67 4fb0ce9 95ecc67 4fb0ce9 95ecc67 4fb0ce9 95ecc67 4fb0ce9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | import { useState, useEffect } from "react";
import {
Loader2,
Rocket,
ShieldCheck,
Brain,
ArrowUpRight,
} from "lucide-react";
import type { LoadingStatus } from "../hooks/LLMContext";
import HfIcon from "./HfIcon";
interface LandingPageProps {
onStart: () => void;
status: LoadingStatus;
isLoading: boolean;
showChat: boolean;
}
const cards = [
{
title: "Step-by-step reasoning",
eyebrow: "REASONING MODEL",
body: "LFM2.5-Thinking generates its reasoning process before producing final answers, improving accuracy on complex tasks like math, coding, and logic.",
Icon: Rocket,
},
{
title: "Private edge inference",
eyebrow: "LOCAL & PRIVATE",
body: "WebGPU-accelerated browser inference ensures high performance. No data is sent to a server, and the demo can even run offline after the initial download.",
Icon: ShieldCheck,
},
{
title: "Scaled reinforcement",
eyebrow: "TRAINING PIPELINE",
body: "The 1.2B parameter model benefits from extended pre-training on 28T tokens and large-scale multi-stage reinforcement learning for best-in-class performance.",
Icon: Brain,
},
] as const;
export function LandingPage({ onStart, status, isLoading, showChat }: LandingPageProps) {
const [introFade, setIntroFade] = useState(true);
useEffect(() => {
const t = setTimeout(() => setIntroFade(false), 50);
return () => clearTimeout(t);
}, []);
const hideMainContent = isLoading || showChat;
const readyToStart = status.state === "ready";
return (
<div className="brand-surface relative flex h-full min-h-full flex-col overflow-x-hidden overflow-y-auto text-black">
<div className="landing-brand-glow absolute inset-0" />
<div
className={`absolute inset-0 z-50 bg-white transition-opacity duration-1000 pointer-events-none ${
introFade ? "opacity-100" : "opacity-0"
}`}
/>
<div
className={`relative z-10 mx-auto flex min-h-full w-full max-w-7xl flex-col px-6 pb-8 pt-6 sm:px-8 sm:pb-10 sm:pt-8 lg:px-14 transition-all duration-700 ${
hideMainContent
? "opacity-0 translate-y-4 pointer-events-none"
: "opacity-100"
}`}
>
<header className="animate-rise-in flex items-start justify-between">
<img
src="/liquid.svg"
alt="Liquid AI"
className="h-10 w-auto sm:h-12"
draggable={false}
/>
<p className="font-support text-[10px] uppercase tracking-[0.22em] text-[#000000b3] sm:text-xs">
LFM2.5 WebGPU Demo
</p>
</header>
<section className="mt-8 flex flex-col items-center text-center sm:mt-12 lg:mt-14">
<div className="animate-rise-in-delayed space-y-5">
<p className="font-support text-xs uppercase tracking-[0.2em] text-[#5505afb3]">
Capable and efficient general-purpose AI systems at every scale
</p>
<h1 className="max-w-3xl text-4xl font-bold leading-[1.04] tracking-tight sm:text-6xl lg:text-7xl">
Capable reasoning.<br />Local inference.<br />WebGPU accelerated.
</h1>
<p className="max-w-2xl mx-auto text-base leading-relaxed text-[#000000b3] sm:text-lg">
Run
<a
href="https://huggingface.co/LiquidAI/LFM2.5-1.2B-Thinking-ONNX"
target="_blank"
rel="noreferrer"
className="mx-1 underline decoration-[#5505af4d] underline-offset-4 hover:text-[#5505af] transition-colors"
>
LFM2.5-1.2B-Thinking
</a>
directly in your browser, powered by
<HfIcon className="size-7 inline-block ml-1 mb-[1px]" />
<a
href="https://github.com/huggingface/transformers.js"
target="_blank"
rel="noreferrer"
className="ml-1 underline decoration-[#5505af4d] underline-offset-4 hover:text-[#5505af] transition-colors"
>
Transformers.js
</a>
</p>
</div>
</section>
<section className="mt-6 flex flex-col gap-4 sm:mt-8 lg:mt-10 lg:flex-row">
{cards.map(({ eyebrow, title, body, Icon }, idx) => (
<article
key={title}
className="animate-rise-in flex-1 flex items-start gap-4 rounded-2xl border border-[#0000001a] bg-[#ffffffcc] px-4 py-4 backdrop-blur-sm sm:gap-5 sm:px-6 sm:py-5"
style={{ animationDelay: `${120 + idx * 90}ms` }}
>
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl border border-[#5505af4d] bg-[linear-gradient(135deg,#5505AF_0%,#CD82F0_55%,#FF5F1E_100%)] text-white">
<Icon className="h-5 w-5" />
</div>
<div className="min-w-0 text-left">
<p className="font-support text-[10px] uppercase tracking-[0.2em] text-[#00000080]">
{eyebrow}
</p>
<h3 className="mt-1 text-xl font-medium leading-tight text-black">
{title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-[#000000b3] sm:text-[15px]">
{body}
</p>
</div>
</article>
))}
</section>
<section className="mt-6 flex flex-col items-center animate-rise-in sm:mt-8 lg:mt-10" style={{ animationDelay: "400ms" }}>
<button
onClick={onStart}
className="inline-flex w-full max-w-sm items-center justify-center gap-2 rounded-xl bg-black px-6 py-3.5 text-base font-semibold text-white transition-transform duration-200 hover:-translate-y-0.5 hover:bg-[#5505af] cursor-pointer"
>
{readyToStart
? "Start chatting"
: "Load model & start chatting"}
<ArrowUpRight className="h-4 w-4" />
</button>
{!readyToStart && (
<p className="mt-3 text-xs text-[#00000080]">
~750 MB will be downloaded and cached locally for future sessions.
</p>
)}
</section>
</div>
<div
className={`brand-surface absolute inset-0 z-20 flex flex-col items-center justify-center transition-opacity duration-700 ${
isLoading ? "opacity-100" : "opacity-0 pointer-events-none"
}`}
>
<div className={`flex w-full max-w-md flex-col items-center px-6 transition-all duration-700 ${isLoading ? "opacity-100 translate-y-0" : "opacity-0 translate-y-4"}`}>
<img
src="/liquid.svg"
alt="Liquid AI"
className="mb-8 h-9 w-auto"
draggable={false}
/>
<Loader2 className="h-10 w-10 animate-spin text-[#5505af]" />
<p className="mt-4 text-sm tracking-wide text-[#000000b3]">
{status.state === "loading"
? (status.message ?? "Loading model…")
: status.state === "error"
? "Error"
: "Initializing…"}
</p>
<div className="mt-4 h-1.5 w-full rounded-full bg-[#0000001a] overflow-hidden">
<div
className="h-full rounded-full bg-[linear-gradient(90deg,#5505AF_0%,#CD82F0_60%,#FF5F1E_100%)] transition-[width] duration-300 ease-out"
style={{
width: `${status.state === "ready" ? 100 : status.state === "loading" && status.progress != null ? status.progress : 0}%`,
}}
/>
</div>
{status.state === "error" && (
<p className="mt-3 text-sm text-red-600">{status.error}</p>
)}
</div>
</div>
</div>
);
}
|