Skip to content

Commit 7553b0a

Browse files
committed
feat(ai): refresh landing ecosystem animation
1 parent 7fd20ef commit 7553b0a

13 files changed

Lines changed: 605 additions & 699 deletions

src/components/AILibraryHero.tsx

Lines changed: 310 additions & 520 deletions
Large diffs are not rendered by default.

src/components/AILibraryHeroBox.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,16 @@ export function AILibraryHeroBox({
3333
opacity = 0.9,
3434
strokeWidth = 3,
3535
fill = 'url(#glassGradientLarge)',
36+
showLogo = true,
3637
logoSize = 40,
38+
centerText = false,
3739
}: AILibraryHeroBoxProps) {
38-
// For centerText, align logo and text higher up; otherwise use normal center
39-
const textX = 25 + logoSize
40-
const textY = 15 + fontSize
40+
const textX = centerText ? width / 2 : 25 + logoSize
41+
const textY = height / 2 + fontSize * 0.35
4142

4243
// Position logo to the right of the text, centered vertically
4344
const logoX = 15
44-
const logoY = 15
45+
const logoY = height / 2 - logoSize / 2
4546

4647
return (
4748
<g transform={`translate(${x}, ${y})`}>
@@ -63,21 +64,23 @@ export function AILibraryHeroBox({
6364
fontFamily="Helvetica"
6465
fontSize={fontSize}
6566
fontWeight={fontWeight}
66-
textAnchor="start"
67+
textAnchor={centerText ? 'middle' : 'start'}
6768
opacity={opacity * 1.05}
6869
>
6970
{label}
7071
</text>
7172
)}
72-
<image
73-
href="/images/logos/logo-color-100.png"
74-
x={logoX}
75-
y={logoY}
76-
width={logoSize}
77-
height={logoSize}
78-
opacity={opacity}
79-
preserveAspectRatio="xMidYMid meet"
80-
/>
73+
{showLogo ? (
74+
<image
75+
href="/images/logos/logo-color-100.png"
76+
x={logoX}
77+
y={logoY}
78+
width={logoSize}
79+
height={logoSize}
80+
opacity={opacity}
81+
preserveAspectRatio="xMidYMid meet"
82+
/>
83+
) : null}
8184
</g>
8285
)
8386
}

src/components/FrameworkCard.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { useCopyButton } from '~/components/CopyMarkdownButton'
66
import { useToast } from '~/components/ToastProvider'
77
import { Check, Copy } from 'lucide-react'
88
import { Card } from '~/components/Card'
9+
import {
10+
getFrameworkDocsHash,
11+
getFrameworkDocsPath,
12+
} from '~/libraries/frameworkSupport'
913

1014
export function FrameworkCard({
1115
framework,
@@ -35,16 +39,8 @@ export function FrameworkCard({
3539
)
3640
})
3741

38-
const hasCustomInstallPath = !!library.installPath
39-
const installationPath = library.installPath
40-
? library.installPath
41-
.replace('$framework', framework.value)
42-
.replace('$libraryId', libraryId)
43-
: 'installation'
44-
45-
// Add framework hash fragment only for default installation pages (when installPath is not defined)
46-
// Link component adds the # automatically, so we just pass the value without #
47-
const installationHash = !hasCustomInstallPath ? framework.value : undefined
42+
const installationPath = getFrameworkDocsPath(framework.value, library)
43+
const installationHash = getFrameworkDocsHash(framework.value, library)
4844

4945
return (
5046
<Card

src/components/landing/AiLanding.tsx

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,68 +54,68 @@ export default function AiLanding() {
5454
A complete AI ecosystem, not a vendor platform
5555
</h3>
5656
<p className="mt-6 text-xl leading-7 opacity-75 max-w-[90vw] sm:max-w-[500px] lg:max-w-[800px]">
57-
TanStack AI is a pure open-source ecosystem of libraries and
58-
standards, not a service. We connect you directly to the AI
59-
providers you choose, with no middleman, no service fees, and no
60-
vendor lock-in. Just powerful, type-safe tools built by and for the
61-
community.
57+
TanStack AI is open-source libraries and AG-UI-compatible standards,
58+
not a hosted gateway. Bring your client framework, your server
59+
runtime, and the AI providers you trust. There is no middleman, no
60+
service fee, and no vendor lock-in, just composable tools built for
61+
teams that want to own their AI stack.
6262
</p>
6363
</div>
6464
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mx-auto max-w-[90vw] sm:max-w-[600px] lg:max-w-[1200px]">
6565
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
66-
<h4 className="text-xl font-bold mb-3">Server Agnostic</h4>
66+
<h4 className="text-xl font-bold mb-3">Client Agnostic</h4>
6767
<p className="opacity-90 text-sm leading-relaxed">
68-
Use any backend server you want. Well-documented protocol with
69-
libraries for TypeScript, PHP, Python, and more.
68+
Use the headless client directly or framework bindings for React,
69+
Vue, Solid, Svelte, and Preact.
7070
</p>
7171
</div>
7272
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
73-
<h4 className="text-xl font-bold mb-3">Client Agnostic</h4>
73+
<h4 className="text-xl font-bold mb-3">AG-UI Native</h4>
7474
<p className="opacity-90 text-sm leading-relaxed">
75-
Vanilla client library (@tanstack/ai-client) or framework
76-
integrations for React, Solid, and more coming soon.
75+
Client-to-server requests and server-to-client streams use AG-UI,
76+
so compatible clients and servers can interoperate.
7777
</p>
7878
</div>
7979
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
80-
<h4 className="text-xl font-bold mb-3">Service Agnostic</h4>
80+
<h4 className="text-xl font-bold mb-3">Server Agnostic</h4>
8181
<p className="opacity-90 text-sm leading-relaxed">
82-
Connect to OpenAI, Anthropic, Gemini, and Ollama out of the box.
83-
Create custom adapters for any provider.
82+
Build endpoints in TypeScript, Python, or PHP with portable
83+
helpers for AG-UI events, SSE, and provider message formats.
8484
</p>
8585
</div>
8686
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
87-
<h4 className="text-xl font-bold mb-3">Full Tooling Support</h4>
87+
<h4 className="text-xl font-bold mb-3">Provider Agnostic</h4>
8888
<p className="opacity-90 text-sm leading-relaxed">
89-
Complete support for client and server tools, including tool
90-
approvals and execution control.
89+
Official adapters cover OpenRouter, OpenAI, Anthropic, Gemini,
90+
Ollama, Groq, Grok/xAI, ElevenLabs, and fal.ai.
9191
</p>
9292
</div>
9393
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
94-
<h4 className="text-xl font-bold mb-3">Thinking & Reasoning</h4>
94+
<h4 className="text-xl font-bold mb-3">Typed Tools</h4>
9595
<p className="opacity-90 text-sm leading-relaxed">
96-
Full support for thinking and reasoning models with
97-
thinking/reasoning tokens streamed to clients.
96+
Define isomorphic tools once, run them on the client or server,
97+
gate them with approvals, and use provider-native tools safely.
9898
</p>
9999
</div>
100100
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
101-
<h4 className="text-xl font-bold mb-3">Fully Type-Safe</h4>
101+
<h4 className="text-xl font-bold mb-3">Model-Aware Types</h4>
102102
<p className="opacity-90 text-sm leading-relaxed">
103-
Complete type safety across providers, models, and model options
104-
from end to end.
103+
Provider and model choices narrow options, tools, modalities, and
104+
structured outputs at compile time.
105105
</p>
106106
</div>
107107
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
108-
<h4 className="text-xl font-bold mb-3">Next-Gen DevTools</h4>
108+
<h4 className="text-xl font-bold mb-3">Media Generation</h4>
109109
<p className="opacity-90 text-sm leading-relaxed">
110-
Amazing developer tools that show you everything happening with
111-
your AI connections in real-time.
110+
Use stable APIs for image, video, speech, transcription, realtime
111+
voice, summarization, and generation hooks.
112112
</p>
113113
</div>
114114
<div className="border border-gray-200 dark:border-gray-800 rounded-xl shadow-xs p-6 transition-all duration-300 ease-out bg-white dark:bg-gray-900 hover:shadow-sm hover:border-pink-500/50 hover:-translate-y-1">
115-
<h4 className="text-xl font-bold mb-3">Pure Open Source</h4>
115+
<h4 className="text-xl font-bold mb-3">Observable Runtime</h4>
116116
<p className="opacity-90 text-sm leading-relaxed">
117-
No hidden service, no fees, no upsells. Community-supported
118-
software connecting you directly to your chosen providers.
117+
Devtools, debug logging, middleware, and observability hooks show
118+
what happened across your AI pipeline.
119119
</p>
120120
</div>
121121
</div>

src/hooks/useAILibraryHeroAnimation.ts

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ import {
66
SERVICE_GUTTER,
77
} from '~/stores/aiLibraryHeroAnimation'
88

9-
const FRAMEWORKS_COUNT = 4
10-
const SERVICES_COUNT = 4
11-
const SERVERS_COUNT = 4
9+
const FRAMEWORKS_COUNT = 6
10+
const SERVICES_COUNT = 9
11+
const SERVERS_COUNT = 3
1212

1313
const MESSAGES = [
1414
{
1515
user: 'What makes TanStack AI different?',
1616
assistant:
17-
'TanStack AI is completely agnostic - server agnostic, client agnostic, and service agnostic. Use any backend (TypeScript, PHP, Python), any client (vanilla JS, React, Solid), and any AI service (OpenAI, Anthropic, Gemini, Ollama). We provide the libraries and standards, you choose your stack.',
17+
'TanStack AI is completely agnostic - client agnostic, protocol agnostic, server agnostic, and provider agnostic. Use Vanilla, React, Vue, Solid, Svelte, or Preact clients, speak AG-UI on the wire, run TypeScript, Python, or PHP servers, and connect to the providers you choose.',
1818
},
1919
{
2020
user: 'Do you support tools?',
2121
assistant:
22-
'Yes! We have full support for both client and server tooling, including tool approvals. You can execute tools on either side with complete type safety and control.',
22+
'Yes. Define tools once, run them on the client or server, require approvals when needed, and use provider-native tools like web search or code execution when the selected model supports them.',
2323
},
2424
{
2525
user: 'What about thinking models?',
@@ -34,12 +34,12 @@ const MESSAGES = [
3434
{
3535
user: 'What about developer experience?',
3636
assistant:
37-
'We have next-generation dev tools that show you everything happening with your AI connection in real-time. Debug, inspect, and optimize with complete visibility.',
37+
'TanStack AI includes devtools, debug logging, middleware, observability hooks, and AG-UI event streams so you can inspect what happened instead of guessing.',
3838
},
3939
{
4040
user: 'Is this a service I have to pay for?',
4141
assistant:
42-
"No! TanStack AI is pure open source software. We don't have a service to promote or charge for. This is an ecosystem of libraries and standards connecting you with the services you choose - completely community supported.",
42+
'No. TanStack AI is open-source software and standards, not a hosted gateway. There is no middleman, no service fee, and no provider lock-in.',
4343
},
4444
]
4545

@@ -151,66 +151,65 @@ export function useAILibraryHeroAnimation() {
151151
: TIMING.rotationSlowBase +
152152
(i - (total - 4)) * TIMING.rotationSlowIncrement,
153153
onComplete: () => {
154-
// Phase: Select service
155-
store.setPhase(AnimationPhase.SELECTING_SERVICE)
156-
const currentService =
157-
useAILibraryHeroAnimationStore.getState().selectedService
158-
const targetService = getRandomIndex(
159-
SERVICES_COUNT,
160-
currentService ?? undefined,
161-
)
162-
const serviceRotations = getRandomRotationCount(6, 3)
163-
164-
let currentServiceIndex = Math.floor(Math.random() * SERVICES_COUNT)
165-
166-
const rotateService = (iteration: number) => {
167-
if (iteration < serviceRotations - 1) {
168-
store.setRotatingService(currentServiceIndex)
169-
currentServiceIndex = (currentServiceIndex + 1) % SERVICES_COUNT
170-
const delay =
171-
iteration < serviceRotations - 3
172-
? TIMING.serviceRotationFast
173-
: TIMING.serviceRotationSlowBase +
174-
(iteration - (serviceRotations - 3)) *
175-
TIMING.serviceRotationSlowIncrement
176-
addTimeout(() => rotateService(iteration + 1), delay)
177-
} else {
178-
store.setRotatingService(targetService)
179-
addTimeout(() => {
180-
store.setSelectedService(targetService)
181-
store.setRotatingService(null)
182-
const targetX = -(
183-
SERVICE_WIDTH / 2 +
184-
SERVICE_GUTTER / 2 +
185-
targetService * (SERVICE_WIDTH + SERVICE_GUTTER)
186-
)
187-
store.setServiceOffset(targetX)
188-
189-
addTimeout(() => {
190-
// Phase: Select server
191-
store.setPhase(AnimationPhase.SELECTING_SERVER)
192-
const targetServer = getRandomIndex(SERVERS_COUNT)
193-
const serverRotations = getRandomRotationCount(8, 4)
194-
195-
runRotation({
196-
count: serverRotations,
197-
setRotating: store.setRotatingServer,
198-
setSelected: store.setSelectedServer,
199-
targetIndex: targetServer,
200-
itemCount: SERVERS_COUNT,
201-
getDelay: (i, total) =>
202-
i < total - 4
203-
? TIMING.rotationFast
204-
: TIMING.rotationSlowBase +
205-
(i - (total - 4)) * TIMING.rotationSlowIncrement,
206-
onComplete,
207-
})
208-
}, TIMING.afterServiceSelection)
209-
}, TIMING.afterSelection)
210-
}
211-
}
212-
213-
rotateService(0)
154+
// Phase: Select server language
155+
store.setPhase(AnimationPhase.SELECTING_SERVER)
156+
const targetServer = getRandomIndex(SERVERS_COUNT)
157+
const serverRotations = getRandomRotationCount(8, 4)
158+
159+
runRotation({
160+
count: serverRotations,
161+
setRotating: store.setRotatingServer,
162+
setSelected: store.setSelectedServer,
163+
targetIndex: targetServer,
164+
itemCount: SERVERS_COUNT,
165+
getDelay: (i, total) =>
166+
i < total - 4
167+
? TIMING.rotationFast
168+
: TIMING.rotationSlowBase +
169+
(i - (total - 4)) * TIMING.rotationSlowIncrement,
170+
onComplete: () => {
171+
// Phase: Select provider
172+
store.setPhase(AnimationPhase.SELECTING_SERVICE)
173+
const currentService =
174+
useAILibraryHeroAnimationStore.getState().selectedService
175+
const targetService = getRandomIndex(
176+
SERVICES_COUNT,
177+
currentService ?? undefined,
178+
)
179+
const serviceRotations = getRandomRotationCount(8, 4)
180+
181+
let currentServiceIndex = Math.floor(
182+
Math.random() * SERVICES_COUNT,
183+
)
184+
185+
const rotateService = (iteration: number) => {
186+
if (iteration < serviceRotations - 1) {
187+
store.setRotatingService(currentServiceIndex)
188+
currentServiceIndex =
189+
(currentServiceIndex + 1) % SERVICES_COUNT
190+
const delay =
191+
iteration < serviceRotations - 4
192+
? TIMING.serviceRotationFast
193+
: TIMING.serviceRotationSlowBase +
194+
(iteration - (serviceRotations - 4)) *
195+
TIMING.serviceRotationSlowIncrement
196+
addTimeout(() => rotateService(iteration + 1), delay)
197+
} else {
198+
store.setRotatingService(targetService)
199+
addTimeout(() => {
200+
store.setSelectedService(targetService)
201+
store.setRotatingService(null)
202+
store.setServiceOffset(
203+
-(targetService * (SERVICE_WIDTH + SERVICE_GUTTER)),
204+
)
205+
addTimeout(onComplete, TIMING.afterServiceSelection)
206+
}, TIMING.afterSelection)
207+
}
208+
}
209+
210+
rotateService(0)
211+
},
212+
})
214213
},
215214
})
216215
}, TIMING.phaseTransition)

src/libraries/ai.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,34 @@ export const aiProject = {
1515
defaultDocs: 'getting-started/overview',
1616
featureHighlights: [
1717
{
18-
title: 'Multi-Provider Support',
18+
title: 'Provider Agnostic',
1919
icon: <Plug className={twMerge(textStyles)} />,
2020
description: (
2121
<div>
22-
Support for OpenAI, Anthropic, Ollama, and Google Gemini. Switch
23-
providers at runtime without code changes. No vendor lock-in, just
24-
clean TypeScript.
22+
Official adapters for OpenRouter, OpenAI, Anthropic, Gemini, Ollama,
23+
Groq, Grok/xAI, ElevenLabs, and fal.ai. Import only the adapters your
24+
app needs.
2525
</div>
2626
),
2727
},
2828
{
29-
title: 'Unified API',
29+
title: 'AG-UI Native Clients',
3030
icon: <Zap className={twMerge(textStyles)} />,
3131
description: (
3232
<div>
33-
Same interface across all providers. Standalone functions with
34-
automatic type inference from adapters. Framework-agnostic client for
35-
any JavaScript environment.
33+
A headless client plus React, Vue, Solid, Svelte, and Preact bindings
34+
all speak the same AG-UI request and event protocol.
3635
</div>
3736
),
3837
},
3938
{
40-
title: 'Tool/Function Calling',
39+
title: 'Typed Tools & Media',
4140
icon: <CogsIcon className={twMerge(textStyles)} />,
4241
description: (
4342
<div>
44-
Automatic execution loop with no manual tool management needed.
45-
Type-safe tool definitions with structured outputs and streaming
46-
support.
43+
Type-safe client/server tools, provider-native tools, structured
44+
output, reasoning streams, image, speech, transcription, realtime
45+
voice, and video generation.
4746
</div>
4847
),
4948
},

0 commit comments

Comments
 (0)