Skip to content

feat(ai): refresh landing ecosystem animation#940

Merged
tannerlinsley merged 2 commits into
mainfrom
ai-landing-refresh
May 22, 2026
Merged

feat(ai): refresh landing ecosystem animation#940
tannerlinsley merged 2 commits into
mainfrom
ai-landing-refresh

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented May 22, 2026

Summary

  • refresh the TanStack AI landing animation around current client frameworks, AG-UI, server languages, and provider adapters
  • update TanStack AI landing copy, ecosystem cards, and feature highlights
  • add AI-specific framework package/docs metadata so framework cards link to existing docs pages instead of 404s

Validation

  • pnpm exec tsx tests/ai-framework-doc-links.test.ts
  • pnpm test

Summary by CodeRabbit

  • New Features

    • Broader framework support (Vue, Svelte, Preact) and themed provider logos (light/dark).
  • Updates

    • Redesigned AI hero: data-driven diagram, smoother highlighting/animations, reflowed layout, SVG clipping, and visual/styling improvements.
    • Landing page refreshed messaging and feature highlights.
    • UI cards accept flexible logo sources with improved layout/defaults.
  • Tests

    • Added tests validating framework package names and docs paths.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors AILibraryHero to data-driven SVG rendering, adds per-framework metadata and helpers, updates animation geometry and selection flow, integrates helpers into framework cards/routes, refreshes marketing content, improves docs filesystem helper, and adds tests for framework metadata.

Changes

AI Hero Data-Driven Refactoring

Layer / File(s) Summary
Framework metadata types and helpers
src/libraries/types.ts, src/libraries/frameworkSupport.ts
LibrarySlim adds optional frameworkPackageNames and frameworkDocs. Exports getFrameworkPackageName, getFrameworkDocsPath, and getFrameworkDocsHash to centralize package name and docs-route/hash logic.
Library configuration with framework mappings
src/libraries/libraries.ts
ai library now lists additional frameworks (vue, svelte, preact) and includes frameworkPackageNames and frameworkDocs mappings.
Animation layout constants and enum reorder
src/stores/aiLibraryHeroAnimation.ts
Reorders AnimationPhase, adjusts SVG_HEIGHT, card/service/server dimensions, gutters, location arrays, Y offsets, and sets initial serviceOffset to 0.
Animation selection flow and timing
src/hooks/useAILibraryHeroAnimation.ts
Updates framework/service/server counts and assistant messages; reorders selection to pick server before service and replaces service rotation with a custom loop and new serviceOffset formula and timing.
Hero component data-driven SVG rendering
src/components/AILibraryHero.tsx
Introduces HeroCardDefinition, CLIENT_FRAMEWORKS/SERVER_LANGUAGES/AI_PROVIDERS arrays, computes isHighlighting/hasActivePath, adds path/pulse helper functions, switches SVG key to include selectedService, uses map()-driven generation for cards/paths, adds providerClip, updates gradients, and expands logo imports.
Hero box component refinements
src/components/AILibraryHeroBox.tsx
Adds explicit defaults for showLogo, logoSize, centerText, centers vertical layout; conditionally renders label and logo; adjusts text anchor and opacity scaling.
Route & FrameworkCard integration with helpers
src/components/FrameworkCard.tsx, src/routes/$libraryId/$version.docs.framework.index.tsx
FrameworkCard and the docs framework route import and use centralized helpers (getFrameworkDocsPath, getFrameworkDocsHash, getFrameworkPackageName) instead of inline path/package logic.
Marketing copy and feature highlights
src/components/landing/AiLanding.tsx, src/libraries/ai.tsx
Replaces AiLanding "AI ecosystem" paragraph and feature highlight cards with a new lineup; updates aiProject.featureHighlights to match.
Documents server base-dir helper
src/utils/documents.server.ts
Adds fileURLToPath import and getLocalRepoBaseDir(repo) helper; fetchFs and fetchApiContentsFs now use this helper for local path computation.
Framework metadata test coverage
tests/ai-framework-doc-links.test.ts
New test verifies ai.frameworks and asserts getFrameworkPackageName and getFrameworkDocsPath produce expected values per framework.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • LadyBluenotes

Poem

🐰 A rabbit hums while SVGs spin bright,

Cards from data, lines pulsing light.
Helpers tie packages and docs in place,
Animations stride with steadier pace.
Cheer — the hero springs to a newer face!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ai): refresh landing ecosystem animation' directly and accurately describes the main change: refreshing the AI landing page's ecosystem animation with updated frameworks, providers, and visual components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ai-landing-refresh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/libraries/ai.tsx (1)

16-49: ⚖️ Poor tradeoff

Consider consolidating overlapping feature content.

The featureHighlights array here partially overlaps with the 8 hardcoded feature cards in AiLanding.tsx (lines 64-121). Topics like "Provider Agnostic," "AG-UI Native," and "Typed Tools" appear in both locations with similar but not identical wording. This duplication creates a maintenance burden if the messaging needs to stay synchronized or evolve together.

Consider whether the two sections could reference a single source of truth, or document the intended distinction between the LibraryFeatureHighlights section and the ecosystem grid cards.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/libraries/ai.tsx` around lines 16 - 49, The featureHighlights array in
src/libraries/ai.tsx duplicates messaging found in the hardcoded feature cards
in AiLanding.tsx; extract the shared feature data into a single exported
constant (e.g., export const LIBRARY_FEATURES) and have both
LibraryFeatureHighlights (featureHighlights) and the AiLanding card grid import
and use that constant, or alternatively add brief comments/descriptions to each
source to document the intended distinction if they must remain different;
update references in the component that currently uses featureHighlights and in
AiLanding.tsx to consume the new shared symbol so messaging is maintained in one
place.
src/components/landing/AiLanding.tsx (1)

66-70: 💤 Low value

Consider mentioning Vanilla framework.

The stack context indicates Vanilla is a supported framework alongside React, Vue, Solid, Svelte, and Preact, but it's not mentioned in the "Client Agnostic" card or elsewhere in the marketing copy. Verify whether Vanilla should be included or if its omission is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/AiLanding.tsx` around lines 66 - 70, The marketing
copy in the AiLanding component’s "Client Agnostic" card omits "Vanilla" from
the list of supported frameworks; update the paragraph text in the AiLanding
component (the <h4> with "Client Agnostic" and its following <p>) to include
"Vanilla" alongside React, Vue, Solid, Svelte, and Preact (e.g., "React, Vue,
Solid, Svelte, Preact, and Vanilla") or, if omission was intentional, add a
brief comment in the AiLanding component explaining why Vanilla is excluded so
reviewers know it’s deliberate.
src/components/AILibraryHero.tsx (1)

243-278: 💤 Low value

pulse-down and pulse-up keyframes are identical—direction has no visual effect.

Both keyframe definitions at lines 243–256 and 257–270 produce the same animation. If the intent is to visually differentiate the pulse direction (e.g., gradient offset, translateY, or brightness sweep direction), the keyframes need distinct definitions. Otherwise, connectionPulseDirection toggling at line 185 is effectively dead logic.

If both directions should look the same, consider consolidating into a single pulse animation to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/AILibraryHero.tsx` around lines 243 - 278, The two keyframes
pulse-down and pulse-up are identical so toggling connectionPulseDirection has
no effect; either make the animations visually different (e.g., change
transform/translateY, gradient offset, or staggered filter/brightness values in
pulse-down vs pulse-up) and keep both keyframes and the classes
(.animate-pulse-down, .animate-pulse-up), or consolidate them into a single
pulse keyframe and replace usages of pulse-down/pulse-up (and the
connectionPulseDirection toggle) with that single animation to remove dead
logic; update the animate-pulse-* class definitions and any code referencing
connectionPulseDirection accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/AILibraryHero.tsx`:
- Around line 243-278: The two keyframes pulse-down and pulse-up are identical
so toggling connectionPulseDirection has no effect; either make the animations
visually different (e.g., change transform/translateY, gradient offset, or
staggered filter/brightness values in pulse-down vs pulse-up) and keep both
keyframes and the classes (.animate-pulse-down, .animate-pulse-up), or
consolidate them into a single pulse keyframe and replace usages of
pulse-down/pulse-up (and the connectionPulseDirection toggle) with that single
animation to remove dead logic; update the animate-pulse-* class definitions and
any code referencing connectionPulseDirection accordingly.

In `@src/components/landing/AiLanding.tsx`:
- Around line 66-70: The marketing copy in the AiLanding component’s "Client
Agnostic" card omits "Vanilla" from the list of supported frameworks; update the
paragraph text in the AiLanding component (the <h4> with "Client Agnostic" and
its following <p>) to include "Vanilla" alongside React, Vue, Solid, Svelte, and
Preact (e.g., "React, Vue, Solid, Svelte, Preact, and Vanilla") or, if omission
was intentional, add a brief comment in the AiLanding component explaining why
Vanilla is excluded so reviewers know it’s deliberate.

In `@src/libraries/ai.tsx`:
- Around line 16-49: The featureHighlights array in src/libraries/ai.tsx
duplicates messaging found in the hardcoded feature cards in AiLanding.tsx;
extract the shared feature data into a single exported constant (e.g., export
const LIBRARY_FEATURES) and have both LibraryFeatureHighlights
(featureHighlights) and the AiLanding card grid import and use that constant, or
alternatively add brief comments/descriptions to each source to document the
intended distinction if they must remain different; update references in the
component that currently uses featureHighlights and in AiLanding.tsx to consume
the new shared symbol so messaging is maintained in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0af7429e-9867-422f-b706-1d270dbadfdf

📥 Commits

Reviewing files that changed from the base of the PR and between 7fd20ef and 7be7176.

📒 Files selected for processing (12)
  • src/components/AILibraryHero.tsx
  • src/components/AILibraryHeroBox.tsx
  • src/components/FrameworkCard.tsx
  • src/components/landing/AiLanding.tsx
  • src/hooks/useAILibraryHeroAnimation.ts
  • src/libraries/ai.tsx
  • src/libraries/frameworkSupport.ts
  • src/libraries/libraries.ts
  • src/libraries/types.ts
  • src/routes/$libraryId/$version.docs.framework.index.tsx
  • src/stores/aiLibraryHeroAnimation.ts
  • tests/ai-framework-doc-links.test.ts

@AlemTuzlak AlemTuzlak force-pushed the ai-landing-refresh branch 2 times, most recently from 7553b0a to 2494633 Compare May 22, 2026 16:37
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/hooks/useAILibraryHeroAnimation.ts (1)

181-210: 💤 Low value

Service rotation duplicates runRotation logic.

The custom service rotation loop (lines 185-207) largely duplicates the runRotation helper with different timing constants. Consider parameterizing runRotation to accept timing configuration, which would reduce duplication and ensure consistent behavior.

That said, the current implementation is functionally correct and the timing differences are intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/useAILibraryHeroAnimation.ts` around lines 181 - 210, The
rotateService function duplicates runRotation logic; refactor by adding
parameters to runRotation (e.g., startIndex, rotations, timingConfig,
targetService, onComplete) and reuse it instead of rotateService: call
runRotation(currentServiceIndex, serviceRotations, {fast:
TIMING.serviceRotationFast, slowBase: TIMING.serviceRotationSlowBase, slowInc:
TIMING.serviceRotationSlowIncrement}, targetService, onComplete) so addTimeout,
store.setRotatingService, index incrementing, final selection behavior and
TIMING-based delay computation live in runRotation and the current rotateService
block is removed; keep references to SERVICES_COUNT, SERVICE_WIDTH,
SERVICE_GUTTER, addTimeout and store.* calls inside runRotation to preserve
behavior.
src/components/AILibraryHero.tsx (1)

502-502: 💤 Low value

Magic numbers in path control points.

The Bezier curve control point Y-coordinates (322 and 424) are hardcoded. Consider deriving these from existing layout constants (e.g., based on SERVER_CARD_Y_OFFSET and SERVICE_Y_OFFSET) for maintainability if the layout geometry changes again.

+const SERVER_TO_TANSTACK_CONTROL_Y = (TANSTACK_AI_BOX.y + TANSTACK_AI_BOX.height + SERVER_CARD_Y_OFFSET) / 2
+const SERVER_TO_PROVIDER_CONTROL_Y = (SERVER_CARD_Y_OFFSET + SERVER_CARD_HEIGHT + SERVICE_Y_OFFSET) / 2

Also applies to: 522-522

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/AILibraryHero.tsx` at line 502, The Bezier path strings use
hardcoded control Y values (e.g., the `d` attribute building string in
AILibraryHero that contains `Q ${serverBoxCenterX} 322 ${endX}
${SERVER_CARD_Y_OFFSET}` and the similar instance around line 522); replace
those magic numbers with computed values derived from existing layout constants
(for example compute controlY as a function of TANSTACK_AI_BOX.y,
TANSTACK_AI_BOX.height, SERVER_CARD_Y_OFFSET, and SERVICE_Y_OFFSET or an
average/midpoint between the source and target Y positions) and use that
computed variable in the template literal so control points adapt when
SERVER_CARD_Y_OFFSET or SERVICE_Y_OFFSET changes.
src/utils/documents.server.ts (1)

129-135: ⚡ Quick win

Consider adding a documentation comment.

The ../../.. navigation assumes a specific directory structure (sibling repositories in local development). A brief comment explaining the path resolution would help future maintainers.

📝 Suggested documentation
+/**
+ * Resolve the base directory for a local repository.
+ * Assumes repos are siblings to the main project:
+ *   parent/tanstack.com/src/utils/documents.server.ts (this file)
+ *   parent/{repo}/ (target repo)
+ */
 function getLocalRepoBaseDir(repo: string) {
   return path.resolve(
     path.dirname(fileURLToPath(import.meta.url)),
     '../../..',
     repo,
   )
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/documents.server.ts` around lines 129 - 135, Add a brief
documentation comment above the getLocalRepoBaseDir function explaining that it
resolves a local sibling repository path by walking up three directories via
'../../..' from the current file (fileURLToPath(import.meta.url)) and that this
assumes the monorepo/project layout used in local development; mention expected
repo layout and a caution that changing project structure requires updating this
relative path. Ensure the comment references getLocalRepoBaseDir and the
'../../..' segment so future maintainers understand the assumption and where to
update if layout changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/landing/AiLanding.tsx`:
- Around line 60-64: The copy in the AiLanding component has a subject-verb
agreement error: the phrase "TanStack AI is open-source libraries and
AG-UI-compatible standards" should be revised so the verb agrees with the plural
compound subject; update the text in the AiLanding component (the paragraph
containing "TanStack AI is open-source libraries and AG-UI-compatible
standards...") to either change "is" to "are" or rephrase to "TanStack AI
consists of open-source libraries and AG-UI-compatible standards" (or similar)
to fix the grammar while preserving the original meaning.

---

Nitpick comments:
In `@src/components/AILibraryHero.tsx`:
- Line 502: The Bezier path strings use hardcoded control Y values (e.g., the
`d` attribute building string in AILibraryHero that contains `Q
${serverBoxCenterX} 322 ${endX} ${SERVER_CARD_Y_OFFSET}` and the similar
instance around line 522); replace those magic numbers with computed values
derived from existing layout constants (for example compute controlY as a
function of TANSTACK_AI_BOX.y, TANSTACK_AI_BOX.height, SERVER_CARD_Y_OFFSET, and
SERVICE_Y_OFFSET or an average/midpoint between the source and target Y
positions) and use that computed variable in the template literal so control
points adapt when SERVER_CARD_Y_OFFSET or SERVICE_Y_OFFSET changes.

In `@src/hooks/useAILibraryHeroAnimation.ts`:
- Around line 181-210: The rotateService function duplicates runRotation logic;
refactor by adding parameters to runRotation (e.g., startIndex, rotations,
timingConfig, targetService, onComplete) and reuse it instead of rotateService:
call runRotation(currentServiceIndex, serviceRotations, {fast:
TIMING.serviceRotationFast, slowBase: TIMING.serviceRotationSlowBase, slowInc:
TIMING.serviceRotationSlowIncrement}, targetService, onComplete) so addTimeout,
store.setRotatingService, index incrementing, final selection behavior and
TIMING-based delay computation live in runRotation and the current rotateService
block is removed; keep references to SERVICES_COUNT, SERVICE_WIDTH,
SERVICE_GUTTER, addTimeout and store.* calls inside runRotation to preserve
behavior.

In `@src/utils/documents.server.ts`:
- Around line 129-135: Add a brief documentation comment above the
getLocalRepoBaseDir function explaining that it resolves a local sibling
repository path by walking up three directories via '../../..' from the current
file (fileURLToPath(import.meta.url)) and that this assumes the monorepo/project
layout used in local development; mention expected repo layout and a caution
that changing project structure requires updating this relative path. Ensure the
comment references getLocalRepoBaseDir and the '../../..' segment so future
maintainers understand the assumption and where to update if layout changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6bb16b72-7339-4bb3-bdff-5f642300afac

📥 Commits

Reviewing files that changed from the base of the PR and between 7553b0a and 2494633.

⛔ Files ignored due to path filters (8)
  • src/images/ag-ui-dark.svg is excluded by !**/*.svg
  • src/images/ag-ui-light.svg is excluded by !**/*.svg
  • src/images/elevenlabs-dark.svg is excluded by !**/*.svg
  • src/images/elevenlabs-light.svg is excluded by !**/*.svg
  • src/images/fal-ai-dark.svg is excluded by !**/*.svg
  • src/images/fal-ai-light.svg is excluded by !**/*.svg
  • src/images/xai-dark.svg is excluded by !**/*.svg
  • src/images/xai-light.svg is excluded by !**/*.svg
📒 Files selected for processing (13)
  • src/components/AILibraryHero.tsx
  • src/components/AILibraryHeroBox.tsx
  • src/components/FrameworkCard.tsx
  • src/components/landing/AiLanding.tsx
  • src/hooks/useAILibraryHeroAnimation.ts
  • src/libraries/ai.tsx
  • src/libraries/frameworkSupport.ts
  • src/libraries/libraries.ts
  • src/libraries/types.ts
  • src/routes/$libraryId/$version.docs.framework.index.tsx
  • src/stores/aiLibraryHeroAnimation.ts
  • src/utils/documents.server.ts
  • tests/ai-framework-doc-links.test.ts

Comment on lines +60 to +64
TanStack AI is open-source libraries and AG-UI-compatible standards,
not a hosted gateway. Bring your client framework, your server
runtime, and the AI providers you trust. There is no middleman, no
service fee, and no vendor lock-in, just composable tools built for
teams that want to own their AI stack.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix subject-verb agreement.

The sentence "TanStack AI is open-source libraries and AG-UI-compatible standards" has a grammatical error: the singular verb "is" doesn't agree with the plural compound subject "libraries and standards."

✏️ Proposed fix
-            TanStack AI is open-source libraries and AG-UI-compatible standards,
+            TanStack AI provides open-source libraries and AG-UI-compatible standards,

Alternatively:

-            TanStack AI is open-source libraries and AG-UI-compatible standards,
+            TanStack AI is a set of open-source libraries and AG-UI-compatible standards,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TanStack AI is open-source libraries and AG-UI-compatible standards,
not a hosted gateway. Bring your client framework, your server
runtime, and the AI providers you trust. There is no middleman, no
service fee, and no vendor lock-in, just composable tools built for
teams that want to own their AI stack.
TanStack AI provides open-source libraries and AG-UI-compatible standards,
not a hosted gateway. Bring your client framework, your server
runtime, and the AI providers you trust. There is no middleman, no
service fee, and no vendor lock-in, just composable tools built for
teams that want to own their AI stack.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/AiLanding.tsx` around lines 60 - 64, The copy in the
AiLanding component has a subject-verb agreement error: the phrase "TanStack AI
is open-source libraries and AG-UI-compatible standards" should be revised so
the verb agrees with the plural compound subject; update the text in the
AiLanding component (the paragraph containing "TanStack AI is open-source
libraries and AG-UI-compatible standards...") to either change "is" to "are" or
rephrase to "TanStack AI consists of open-source libraries and AG-UI-compatible
standards" (or similar) to fix the grammar while preserving the original
meaning.

@AlemTuzlak AlemTuzlak force-pushed the ai-landing-refresh branch 2 times, most recently from 0c0340a to b436c01 Compare May 22, 2026 16:59
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
src/components/landing/AiLanding.tsx (1)

60-64: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix subject-verb agreement.

The sentence has a grammatical error: the singular verb "is" doesn't agree with the plural compound subject "libraries and standards."

✏️ Proposed fix
-            TanStack AI is open-source libraries and AG-UI-compatible standards,
+            TanStack AI provides open-source libraries and AG-UI-compatible standards,

Alternatively:

-            TanStack AI is open-source libraries and AG-UI-compatible standards,
+            TanStack AI is a set of open-source libraries and AG-UI-compatible standards,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/AiLanding.tsx` around lines 60 - 64, In the AiLanding
component update the sentence so subject and verb agree: change "TanStack AI is
open-source libraries and AG-UI-compatible standards" to use a plural verb or
rephrase (e.g., "TanStack AI are open-source libraries and AG-UI-compatible
standards" or better "TanStack AI comprises open-source libraries and
AG-UI-compatible standards")—edit the string in the AiLanding component where
that paragraph is defined to use the corrected wording.
🧹 Nitpick comments (2)
src/libraries/frameworkSupport.ts (1)

3-23: ⚡ Quick win

Remove libraryId from helper input to avoid split-source mismatches.

getFrameworkPackageName can derive everything from library.id; taking both libraryId and library makes incorrect pairings possible and can silently produce wrong package names.

♻️ Suggested change
 export function getFrameworkPackageName(
   framework: Framework,
-  libraryId: LibraryId,
   library: LibrarySlim,
 ) {
   const packageName = library.frameworkPackageNames?.[framework]

   if (packageName) {
     return packageName
   }

   if (framework === 'vanilla') {
-    return library.corePackageName ?? `@tanstack/${libraryId}`
+    return library.corePackageName ?? `@tanstack/${library.id}`
   }

-  if (framework === 'angular' && libraryId === 'query') {
+  if (framework === 'angular' && library.id === 'query') {
     return '`@tanstack/angular-query-experimental`'
   }

-  return `@tanstack/${framework}-${libraryId}`
+  return `@tanstack/${framework}-${library.id}`
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/libraries/frameworkSupport.ts` around lines 3 - 23, The helper
getFrameworkPackageName currently accepts both libraryId and library which
allows mismatched pairs; change its signature to only accept (framework:
Framework, library: LibrarySlim) and derive the id from library.id everywhere
inside the function (use library.id in place of the former libraryId variable)
while preserving the existing special-casing for 'vanilla' and the angular query
exception; then update all call sites that passed a separate libraryId to pass
only the library and framework (or extract library.id at the call site) so
callers match the new signature and no longer risk inconsistent
libraryId/library pairs.
tests/ai-framework-doc-links.test.ts (1)

27-54: 🏗️ Heavy lift

Add an existence check for each docs path, not just string equality.

This test currently verifies configured mappings but doesn’t verify that each resolved docs path actually exists, so 404 regressions can still slip through while this test passes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ai-framework-doc-links.test.ts` around lines 27 - 54, The test only
compares configured strings (expectedDocsPaths) but doesn't assert those docs
paths actually exist; update the loop that iterates expectedFrameworks to, after
computing docsPath = getFrameworkDocsPath(framework, ai), resolve that logical
docs path to the repo docs file (e.g. join process.cwd() with the docs root and
check for either `${docsPath}.md` or `${docsPath}/index.md`) and add an
assertion like assert.ok(fs.existsSync(resolvedPath), `Docs page for
${framework} exists`); you can encapsulate the filesystem check in a small
helper (e.g. docsPathExists or resolveDocsFile) to keep the test readable and
use Node's fs and path modules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/AILibraryHero.tsx`:
- Around line 254-263: getProviderOpacity currently forces provider cards to be
fully inside the viewport before showing them, causing pop-in/out behavior;
change its logic to only hide when completely off-screen and otherwise return
getServiceOpacity so the SVG clipPath can handle edge clipping. Update
getProviderOpacity (referencing SERVICE_LOCATIONS, serviceOffset, SERVICE_WIDTH,
SVG_WIDTH, and getServiceOpacity) to compute x = SERVICE_LOCATIONS[index] +
serviceOffset and return 0 only when the card is entirely outside the lane
(e.g., x + SERVICE_WIDTH <= 0 or x >= SVG_WIDTH); otherwise return
getServiceOpacity(index).

In `@src/components/AILibraryHeroBox.tsx`:
- Around line 39-49: The left-aligned text is still indented because textX
always adds logoSize; update the textX calculation in AILibraryHeroBox to only
add the logo offset when a logo will actually be rendered (i.e., when showLogo
is true and a custom/separate logo exists via hasCustomLogo or
hasSeparateLogos), otherwise omit the logoSize so left-aligned labels are flush;
adjust the expression that computes textX (and any related layout that uses
logoSize) to guard with showLogo && (hasCustomLogo || hasSeparateLogos).

In `@src/hooks/useAILibraryHeroAnimation.ts`:
- Around line 185-205: The rotation currently only updates serviceOffset after
all steps, causing intermediate rotatingService indices to be off-canvas; modify
rotateService so that on every iteration (inside rotateService) you call
store.setServiceOffset with the offset computed from the current rotating index
(use -(currentIndex * (SERVICE_WIDTH + SERVICE_GUTTER))) so the active provider
stays centered during each step, and ensure you also set the offset for the
final targetService before the post-selection pause (right before calling
setSelectedService and setRotatingService(null)); keep existing
setRotatingService calls and timing logic but add the per-step setServiceOffset
updates using the same offset formula.

In `@src/utils/documents.server.ts`:
- Around line 129-135: The getLocalRepoBaseDir function must validate the repo
parameter to prevent path traversal: reject or throw for repo values containing
"..", absolute paths, path separators that escape the expected "owner/repo"
form, or any characters outside an allowed pattern (e.g., only letters, numbers,
dots, hyphens, underscores and a single slash). Update getLocalRepoBaseDir to
validate repo first (and fail-fast) and/or normalize and re-check that the
resolved path remains inside the intended repo root (so downstream checks like
localFilePath.startsWith(baseDir) are meaningful); reference getLocalRepoBaseDir
when adding the validation and ensure callers cannot pass an unsafe repo string.

---

Duplicate comments:
In `@src/components/landing/AiLanding.tsx`:
- Around line 60-64: In the AiLanding component update the sentence so subject
and verb agree: change "TanStack AI is open-source libraries and
AG-UI-compatible standards" to use a plural verb or rephrase (e.g., "TanStack AI
are open-source libraries and AG-UI-compatible standards" or better "TanStack AI
comprises open-source libraries and AG-UI-compatible standards")—edit the string
in the AiLanding component where that paragraph is defined to use the corrected
wording.

---

Nitpick comments:
In `@src/libraries/frameworkSupport.ts`:
- Around line 3-23: The helper getFrameworkPackageName currently accepts both
libraryId and library which allows mismatched pairs; change its signature to
only accept (framework: Framework, library: LibrarySlim) and derive the id from
library.id everywhere inside the function (use library.id in place of the former
libraryId variable) while preserving the existing special-casing for 'vanilla'
and the angular query exception; then update all call sites that passed a
separate libraryId to pass only the library and framework (or extract library.id
at the call site) so callers match the new signature and no longer risk
inconsistent libraryId/library pairs.

In `@tests/ai-framework-doc-links.test.ts`:
- Around line 27-54: The test only compares configured strings
(expectedDocsPaths) but doesn't assert those docs paths actually exist; update
the loop that iterates expectedFrameworks to, after computing docsPath =
getFrameworkDocsPath(framework, ai), resolve that logical docs path to the repo
docs file (e.g. join process.cwd() with the docs root and check for either
`${docsPath}.md` or `${docsPath}/index.md`) and add an assertion like
assert.ok(fs.existsSync(resolvedPath), `Docs page for ${framework} exists`); you
can encapsulate the filesystem check in a small helper (e.g. docsPathExists or
resolveDocsFile) to keep the test readable and use Node's fs and path modules.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a956060-d586-496b-9daf-144012013f1c

📥 Commits

Reviewing files that changed from the base of the PR and between 2494633 and 0c0340a.

⛔ Files ignored due to path filters (10)
  • src/images/ag-ui-dark.svg is excluded by !**/*.svg
  • src/images/ag-ui-light.svg is excluded by !**/*.svg
  • src/images/elevenlabs-dark.svg is excluded by !**/*.svg
  • src/images/elevenlabs-light.svg is excluded by !**/*.svg
  • src/images/fal-ai-dark.svg is excluded by !**/*.svg
  • src/images/fal-ai-light.svg is excluded by !**/*.svg
  • src/images/groq-dark.svg is excluded by !**/*.svg
  • src/images/groq-light.svg is excluded by !**/*.svg
  • src/images/xai-dark.svg is excluded by !**/*.svg
  • src/images/xai-light.svg is excluded by !**/*.svg
📒 Files selected for processing (13)
  • src/components/AILibraryHero.tsx
  • src/components/AILibraryHeroBox.tsx
  • src/components/FrameworkCard.tsx
  • src/components/landing/AiLanding.tsx
  • src/hooks/useAILibraryHeroAnimation.ts
  • src/libraries/ai.tsx
  • src/libraries/frameworkSupport.ts
  • src/libraries/libraries.ts
  • src/libraries/types.ts
  • src/routes/$libraryId/$version.docs.framework.index.tsx
  • src/stores/aiLibraryHeroAnimation.ts
  • src/utils/documents.server.ts
  • tests/ai-framework-doc-links.test.ts
✅ Files skipped from review due to trivial changes (1)
  • src/libraries/ai.tsx

Comment thread src/components/AILibraryHero.tsx
Comment on lines +39 to +49
showLogo = true,
logoLight,
logoDark,
logo,
logoSize = 40,
centerText = false,
}: AILibraryHeroBoxProps) {
// For centerText, align logo and text higher up; otherwise use normal center
const textX = 25 + logoSize
const textY = 15 + fontSize
const hasCustomLogo = logo || logoLight || logoDark
const hasSeparateLogos = !logo && logoLight && logoDark
const textX = centerText ? width / 2 : 25 + logoSize
const textY = height / 2 + fontSize * 0.35
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

showLogo={false} still leaves the label indented.

The new prop hides the image, but textX still reserves logoSize, so left-aligned labels render shifted right when the logo is disabled.

Suggested diff
-  const textX = centerText ? width / 2 : 25 + logoSize
+  const hasVisibleLogo = showLogo
+  const textX = centerText ? width / 2 : hasVisibleLogo ? 25 + logoSize : 25
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
showLogo = true,
logoLight,
logoDark,
logo,
logoSize = 40,
centerText = false,
}: AILibraryHeroBoxProps) {
// For centerText, align logo and text higher up; otherwise use normal center
const textX = 25 + logoSize
const textY = 15 + fontSize
const hasCustomLogo = logo || logoLight || logoDark
const hasSeparateLogos = !logo && logoLight && logoDark
const textX = centerText ? width / 2 : 25 + logoSize
const textY = height / 2 + fontSize * 0.35
showLogo = true,
logoLight,
logoDark,
logo,
logoSize = 40,
centerText = false,
}: AILibraryHeroBoxProps) {
const hasCustomLogo = logo || logoLight || logoDark
const hasSeparateLogos = !logo && logoLight && logoDark
const hasVisibleLogo = showLogo
const textX = centerText ? width / 2 : hasVisibleLogo ? 25 + logoSize : 25
const textY = height / 2 + fontSize * 0.35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/AILibraryHeroBox.tsx` around lines 39 - 49, The left-aligned
text is still indented because textX always adds logoSize; update the textX
calculation in AILibraryHeroBox to only add the logo offset when a logo will
actually be rendered (i.e., when showLogo is true and a custom/separate logo
exists via hasCustomLogo or hasSeparateLogos), otherwise omit the logoSize so
left-aligned labels are flush; adjust the expression that computes textX (and
any related layout that uses logoSize) to guard with showLogo && (hasCustomLogo
|| hasSeparateLogos).

Comment thread src/hooks/useAILibraryHeroAnimation.ts
Comment on lines +129 to +135
function getLocalRepoBaseDir(repo: string) {
return path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../..',
repo,
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate repo parameter to prevent path traversal.

If repo contains .. segments (e.g., from a malicious repoPair like "TanStack/../../etc"), the returned base directory escapes the intended scope. Downstream checks like localFilePath.startsWith(baseDir) in fetchFs become ineffective because baseDir itself is compromised.

🛡️ Proposed fix: Add validation
 function getLocalRepoBaseDir(repo: string) {
+  const normalized = path.normalize(repo)
+  if (normalized.startsWith('..') || normalized.includes('/..') || path.isAbsolute(normalized)) {
+    throw new Error(`Invalid repo name: ${repo}`)
+  }
   return path.resolve(
     path.dirname(fileURLToPath(import.meta.url)),
     '../../..',
     repo,
   )
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function getLocalRepoBaseDir(repo: string) {
return path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../..',
repo,
)
}
function getLocalRepoBaseDir(repo: string) {
const normalized = path.normalize(repo)
if (normalized.startsWith('..') || normalized.includes('/..') || path.isAbsolute(normalized)) {
throw new Error(`Invalid repo name: ${repo}`)
}
return path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'../../..',
repo,
)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/utils/documents.server.ts` around lines 129 - 135, The
getLocalRepoBaseDir function must validate the repo parameter to prevent path
traversal: reject or throw for repo values containing "..", absolute paths, path
separators that escape the expected "owner/repo" form, or any characters outside
an allowed pattern (e.g., only letters, numbers, dots, hyphens, underscores and
a single slash). Update getLocalRepoBaseDir to validate repo first (and
fail-fast) and/or normalize and re-check that the resolved path remains inside
the intended repo root (so downstream checks like
localFilePath.startsWith(baseDir) are meaningful); reference getLocalRepoBaseDir
when adding the validation and ensure callers cannot pass an unsafe repo string.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/ai-framework-doc-links.test.ts (1)

4-7: ⚡ Quick win

Add coverage for getFrameworkDocsHash to lock full link behavior.

FrameworkCard now depends on docs path + hash together. Extending this test to assert hash behavior will prevent regressions where links resolve to wrong anchors.

Proposed test extension
 import {
+  getFrameworkDocsHash,
   getFrameworkDocsPath,
   getFrameworkPackageName,
 } from '../src/libraries/frameworkSupport'

+const expectedDocsHashes: Partial<Record<Framework, string | undefined>> = {
+  react: undefined,
+  vue: undefined,
+  solid: undefined,
+  svelte: undefined,
+  preact: undefined,
+  vanilla: undefined,
+}
+
 for (const framework of expectedFrameworks) {
   assert.equal(
     getFrameworkPackageName(framework, ai.id, ai),
     expectedPackages[framework],
     `${framework} package name points to the shipped AI package`,
   )

   assert.equal(
     getFrameworkDocsPath(framework, ai),
     expectedDocsPaths[framework],
     `${framework} framework card links to an existing AI docs page`,
   )
+
+  assert.equal(
+    getFrameworkDocsHash(framework, ai),
+    expectedDocsHashes[framework],
+    `${framework} docs hash matches framework-link contract`,
+  )
 }

Also applies to: 42-54

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ai-framework-doc-links.test.ts` around lines 4 - 7, Extend the existing
test to call getFrameworkDocsHash in addition to getFrameworkDocsPath and
getFrameworkPackageName and assert that FrameworkCard-style links are built
using the docs path concatenated with the returned hash (i.e., verify
`${getFrameworkDocsPath(...) }#${ getFrameworkDocsHash(...) }` or equivalent is
produced); update the test to cover multiple frameworks (reuse existing test
inputs) and add assertions that the final link equals the expected path+hash
string so both path and hash behavior are locked down (refer to functions
getFrameworkDocsHash, getFrameworkDocsPath, getFrameworkPackageName and the
FrameworkCard link behavior).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/ai-framework-doc-links.test.ts`:
- Around line 27-53: The test currently only checks getFrameworkDocsPath against
hardcoded expectedDocsPaths but doesn't assert the target docs actually exist;
update the test to load the docs route/index used by the site build (the same
source the site uses) and for each framework call
getFrameworkDocsPath(framework, ai) and assert that the returned path is present
in that route index (e.g. docsRoutes.includes(resolvedPath) or
routeIndex.has(resolvedPath)); keep the existing assertions but add this
existence check with a clear failure message referencing the framework and
resolvedPath so renamed/removed pages fail the test; locate this change next to
the loop that iterates expectedFrameworks and uses getFrameworkDocsPath.

---

Nitpick comments:
In `@tests/ai-framework-doc-links.test.ts`:
- Around line 4-7: Extend the existing test to call getFrameworkDocsHash in
addition to getFrameworkDocsPath and getFrameworkPackageName and assert that
FrameworkCard-style links are built using the docs path concatenated with the
returned hash (i.e., verify `${getFrameworkDocsPath(...) }#${
getFrameworkDocsHash(...) }` or equivalent is produced); update the test to
cover multiple frameworks (reuse existing test inputs) and add assertions that
the final link equals the expected path+hash string so both path and hash
behavior are locked down (refer to functions getFrameworkDocsHash,
getFrameworkDocsPath, getFrameworkPackageName and the FrameworkCard link
behavior).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: afeb967d-ebc3-41fb-b20f-999d49ef6d49

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0340a and b436c01.

⛔ Files ignored due to path filters (10)
  • src/images/ag-ui-dark.svg is excluded by !**/*.svg
  • src/images/ag-ui-light.svg is excluded by !**/*.svg
  • src/images/elevenlabs-dark.svg is excluded by !**/*.svg
  • src/images/elevenlabs-light.svg is excluded by !**/*.svg
  • src/images/fal-ai-dark.svg is excluded by !**/*.svg
  • src/images/fal-ai-light.svg is excluded by !**/*.svg
  • src/images/groq-dark.svg is excluded by !**/*.svg
  • src/images/groq-light.svg is excluded by !**/*.svg
  • src/images/xai-dark.svg is excluded by !**/*.svg
  • src/images/xai-light.svg is excluded by !**/*.svg
📒 Files selected for processing (13)
  • src/components/AILibraryHero.tsx
  • src/components/AILibraryHeroBox.tsx
  • src/components/FrameworkCard.tsx
  • src/components/landing/AiLanding.tsx
  • src/hooks/useAILibraryHeroAnimation.ts
  • src/libraries/ai.tsx
  • src/libraries/frameworkSupport.ts
  • src/libraries/libraries.ts
  • src/libraries/types.ts
  • src/routes/$libraryId/$version.docs.framework.index.tsx
  • src/stores/aiLibraryHeroAnimation.ts
  • src/utils/documents.server.ts
  • tests/ai-framework-doc-links.test.ts

Comment on lines +27 to +53
const expectedDocsPaths: Partial<Record<Framework, string>> = {
react: 'getting-started/quick-start',
vue: 'getting-started/quick-start-vue',
solid: 'api/ai-solid',
svelte: 'getting-started/quick-start-svelte',
preact: 'api/ai-preact',
vanilla: 'api/ai-client',
}

assert.deepEqual(
ai.frameworks,
expectedFrameworks,
'AI framework list reflects shipped framework packages',
)

for (const framework of expectedFrameworks) {
assert.equal(
getFrameworkPackageName(framework, ai.id, ai),
expectedPackages[framework],
`${framework} package name points to the shipped AI package`,
)

assert.equal(
getFrameworkDocsPath(framework, ai),
expectedDocsPaths[framework],
`${framework} framework card links to an existing AI docs page`,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Test does not verify docs routes actually exist.

These assertions only validate helper output against hardcoded strings, so the test still passes if a mapped page is deleted/renamed and links 404 in production. Please add an existence check for each resolved docs path against the docs source/route index used by the site build.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/ai-framework-doc-links.test.ts` around lines 27 - 53, The test
currently only checks getFrameworkDocsPath against hardcoded expectedDocsPaths
but doesn't assert the target docs actually exist; update the test to load the
docs route/index used by the site build (the same source the site uses) and for
each framework call getFrameworkDocsPath(framework, ai) and assert that the
returned path is present in that route index (e.g.
docsRoutes.includes(resolvedPath) or routeIndex.has(resolvedPath)); keep the
existing assertions but add this existence check with a clear failure message
referencing the framework and resolvedPath so renamed/removed pages fail the
test; locate this change next to the loop that iterates expectedFrameworks and
uses getFrameworkDocsPath.

@AlemTuzlak AlemTuzlak force-pushed the ai-landing-refresh branch 4 times, most recently from 59613d2 to c72d7eb Compare May 22, 2026 17:34
@AlemTuzlak AlemTuzlak force-pushed the ai-landing-refresh branch from c72d7eb to e773b52 Compare May 22, 2026 17:37
@tannerlinsley tannerlinsley merged commit 8faf458 into main May 22, 2026
4 checks passed
@tannerlinsley tannerlinsley deleted the ai-landing-refresh branch May 22, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants