From f6d0134ce19818ac69bfca1399555047f4a1f9e9 Mon Sep 17 00:00:00 2001 From: Artur Czemiel Date: Fri, 24 Jul 2026 23:52:40 +0200 Subject: [PATCH 1/8] Add GraphQL & AI subpage (/ai) with interactive demos New /ai route showcasing GraphQL for AI systems (MCP servers, RAG, agents & tool calling). Includes hero with canvas particle network, interactive Star Wars GraphQL demo executing real queries client-side, syntax-highlighted code blocks, by-the-numbers stats, and CTA. - src/app/(main)/ai/: page + 9 components (hero, interactive-demo, how-it-works, why-graphql-ai, use-cases, by-the-numbers, cta-community, syntax-highlight) - src/pages/_meta.tsx: add 'ai' nav entry pointing to /ai - src/globals.css: add @keyframes fadeInUp used by interactive demo - package.json + pnpm-lock.yaml: add sharp dependency --- package.json | 1 + pnpm-lock.yaml | 270 +++---- .../(main)/ai/components/by-the-numbers.tsx | 192 +++++ .../(main)/ai/components/cta-community.tsx | 41 ++ src/app/(main)/ai/components/hero.tsx | 193 +++++ src/app/(main)/ai/components/how-it-works.tsx | 181 +++++ .../(main)/ai/components/interactive-demo.tsx | 660 ++++++++++++++++++ .../(main)/ai/components/syntax-highlight.tsx | 314 +++++++++ src/app/(main)/ai/components/use-cases.tsx | 171 +++++ .../(main)/ai/components/why-graphql-ai.tsx | 141 ++++ src/app/(main)/ai/page.tsx | 35 + src/globals.css | 11 + src/pages/_meta.tsx | 9 + 13 files changed, 2096 insertions(+), 123 deletions(-) create mode 100644 src/app/(main)/ai/components/by-the-numbers.tsx create mode 100644 src/app/(main)/ai/components/cta-community.tsx create mode 100644 src/app/(main)/ai/components/hero.tsx create mode 100644 src/app/(main)/ai/components/how-it-works.tsx create mode 100644 src/app/(main)/ai/components/interactive-demo.tsx create mode 100644 src/app/(main)/ai/components/syntax-highlight.tsx create mode 100644 src/app/(main)/ai/components/use-cases.tsx create mode 100644 src/app/(main)/ai/components/why-graphql-ai.tsx create mode 100644 src/app/(main)/ai/page.tsx diff --git a/package.json b/package.json index 9d11ac361a..61476a7020 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "rss": "1.2.2", "scroll-into-view-if-needed": "^3.1.0", "server-only": "0.0.1", + "sharp": "^0.34.5", "string-similarity": "^4.0.4", "string-strip-html": "^13.4.8", "tailwindcss": "^3.4.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5abe88ba5..66c36a77bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,7 +70,7 @@ importers: version: 15.5.9 '@plaiceholder/next': specifier: ^3.0.0 - version: 3.0.0(next@14.2.35(@babel/core@7.28.3)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(plaiceholder@3.0.0(sharp@0.34.4))(sharp@0.34.4) + version: 3.0.0(next@14.2.35(@babel/core@7.28.3)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(plaiceholder@3.0.0(sharp@0.34.5))(sharp@0.34.5) '@sparticuz/chromium': specifier: ^138.0.2 version: 138.0.2 @@ -163,7 +163,7 @@ importers: version: 1.6.4 plaiceholder: specifier: ^3.0.0 - version: 3.0.0(sharp@0.34.4) + version: 3.0.0(sharp@0.34.5) playwright-core: specifier: ^1.54.2 version: 1.57.0 @@ -200,6 +200,9 @@ importers: server-only: specifier: 0.0.1 version: 0.0.1 + sharp: + specifier: ^0.34.5 + version: 0.34.5 string-similarity: specifier: ^4.0.4 version: 4.0.4 @@ -1606,124 +1609,135 @@ packages: resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.34.4': - resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.4': - resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.3': - resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.3': - resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.3': - resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.3': - resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.3': - resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.3': - resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.3': - resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': - resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.3': - resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.4': - resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.4': - resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.4': - resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.4': - resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.4': - resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.4': - resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.4': - resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.4': - resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.4': - resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.4': - resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.4': - resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -5897,8 +5911,8 @@ packages: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} - sharp@0.34.4: - resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -8403,90 +8417,98 @@ snapshots: '@img/colour@1.0.0': {} - '@img/sharp-darwin-arm64@0.34.4': + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.4': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.3': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.3': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.3': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.3': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.3': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.3': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.3': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.3': + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm64@0.34.4': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.4': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.4': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.4': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.4': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.4': + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.4': + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true - '@img/sharp-wasm32@0.34.4': + '@img/sharp-wasm32@0.34.5': dependencies: '@emnapi/runtime': 1.8.1 optional: true - '@img/sharp-win32-arm64@0.34.4': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-ia32@0.34.4': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@img/sharp-win32-x64@0.34.4': + '@img/sharp-win32-x64@0.34.5': optional: true '@inquirer/ansi@1.0.1': {} @@ -8819,7 +8841,7 @@ snapshots: ini: 4.1.3 nopt: 7.2.1 proc-log: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 walk-up-path: 3.0.1 transitivePeerDependencies: - bluebird @@ -8833,7 +8855,7 @@ snapshots: proc-log: 4.2.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.7.2 + semver: 7.7.3 which: 4.0.0 transitivePeerDependencies: - bluebird @@ -8855,7 +8877,7 @@ snapshots: json-parse-even-better-errors: 3.0.2 normalize-package-data: 6.0.2 proc-log: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 transitivePeerDependencies: - bluebird @@ -8868,11 +8890,11 @@ snapshots: '@pkgr/core@0.2.9': {} - '@plaiceholder/next@3.0.0(next@14.2.35(@babel/core@7.28.3)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(plaiceholder@3.0.0(sharp@0.34.4))(sharp@0.34.4)': + '@plaiceholder/next@3.0.0(next@14.2.35(@babel/core@7.28.3)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(plaiceholder@3.0.0(sharp@0.34.5))(sharp@0.34.5)': dependencies: next: 14.2.35(@babel/core@7.28.3)(@playwright/test@1.57.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - plaiceholder: 3.0.0(sharp@0.34.4) - sharp: 0.34.4 + plaiceholder: 3.0.0(sharp@0.34.5) + sharp: 0.34.5 '@playwright/test@1.57.0': dependencies: @@ -9825,7 +9847,7 @@ snapshots: path-case: 3.0.4 sentence-case: 3.0.4 snake-case: 3.0.4 - tslib: 2.6.3 + tslib: 2.8.1 character-entities-html4@2.1.0: {} @@ -11481,7 +11503,7 @@ snapshots: is-lower-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 is-map@2.0.3: {} @@ -11548,7 +11570,7 @@ snapshots: is-upper-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 is-weakmap@2.0.2: {} @@ -11804,7 +11826,7 @@ snapshots: lower-case-first@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 lower-case@2.0.2: dependencies: @@ -12599,7 +12621,7 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-license: 3.0.4 normalize-path@2.1.1: @@ -12610,7 +12632,7 @@ snapshots: npm-install-checks@6.3.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 npm-normalize-package-bin@3.0.1: {} @@ -12618,7 +12640,7 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-name: 5.0.1 npm-pick-manifest@9.1.0: @@ -12626,7 +12648,7 @@ snapshots: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 11.0.3 - semver: 7.7.2 + semver: 7.7.3 npm-run-path@5.3.0: dependencies: @@ -12878,9 +12900,9 @@ snapshots: exsolve: 1.0.7 pathe: 2.0.3 - plaiceholder@3.0.0(sharp@0.34.4): + plaiceholder@3.0.0(sharp@0.34.5): dependencies: - sharp: 0.34.4 + sharp: 0.34.5 playwright-core@1.57.0: {} @@ -13521,34 +13543,36 @@ snapshots: dependencies: kind-of: 6.0.3 - sharp@0.34.4: + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 detect-libc: 2.1.2 semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.4 - '@img/sharp-darwin-x64': 0.34.4 - '@img/sharp-libvips-darwin-arm64': 1.2.3 - '@img/sharp-libvips-darwin-x64': 1.2.3 - '@img/sharp-libvips-linux-arm': 1.2.3 - '@img/sharp-libvips-linux-arm64': 1.2.3 - '@img/sharp-libvips-linux-ppc64': 1.2.3 - '@img/sharp-libvips-linux-s390x': 1.2.3 - '@img/sharp-libvips-linux-x64': 1.2.3 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 - '@img/sharp-libvips-linuxmusl-x64': 1.2.3 - '@img/sharp-linux-arm': 0.34.4 - '@img/sharp-linux-arm64': 0.34.4 - '@img/sharp-linux-ppc64': 0.34.4 - '@img/sharp-linux-s390x': 0.34.4 - '@img/sharp-linux-x64': 0.34.4 - '@img/sharp-linuxmusl-arm64': 0.34.4 - '@img/sharp-linuxmusl-x64': 0.34.4 - '@img/sharp-wasm32': 0.34.4 - '@img/sharp-win32-arm64': 0.34.4 - '@img/sharp-win32-ia32': 0.34.4 - '@img/sharp-win32-x64': 0.34.4 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 shebang-command@2.0.0: dependencies: @@ -13652,7 +13676,7 @@ snapshots: sponge-case@1.0.1: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 sprintf-js@1.0.3: {} @@ -13840,7 +13864,7 @@ snapshots: swap-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 symbol-tree@3.2.4: {} @@ -13933,7 +13957,7 @@ snapshots: title-case@3.0.3: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 title@4.0.1: dependencies: @@ -14234,11 +14258,11 @@ snapshots: upper-case-first@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 upper-case@2.0.2: dependencies: - tslib: 2.6.3 + tslib: 2.8.1 uri-js@4.4.1: dependencies: diff --git a/src/app/(main)/ai/components/by-the-numbers.tsx b/src/app/(main)/ai/components/by-the-numbers.tsx new file mode 100644 index 0000000000..96277f1dff --- /dev/null +++ b/src/app/(main)/ai/components/by-the-numbers.tsx @@ -0,0 +1,192 @@ +"use client" + +import { useState, useEffect, useRef } from "react" +import { SectionLabel } from "@/app/conf/_design-system/section-label" +import ArrowUpIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" + +const stats = [ + { + label: "Token reduction", + graphQL: "90%", + graphQLDesc: "fewer tokens", + rest: "10× more", + restDesc: "token waste", + explanation: + "GraphQL lets AI agents request only the fields they need. REST endpoints return fixed payloads — often 10x the data an LLM actually needs to process. Every extra token costs money and context window space.", + }, + { + label: "API calls per task", + graphQL: "1", + graphQLDesc: "single request", + rest: "3–7", + restDesc: "sequential calls", + explanation: + "GraphQL's composability means agents can fetch nested, related data in one query. REST requires multiple endpoints, forcing agents to make sequential calls and stitch responses client-side.", + }, + { + label: "Tool definitions", + graphQL: "0", + graphQLDesc: "auto-discovered", + rest: "manual", + restDesc: "per endpoint", + explanation: + "Every GraphQL API includes built-in introspection. Agents can discover available types, fields, and arguments automatically. REST tool calling requires hand-crafted JSON Schema definitions for every endpoint.", + }, + { + label: "Type safety", + graphQL: "100%", + graphQLDesc: "typed responses", + rest: "ad-hoc", + restDesc: "no guarantees", + explanation: + "GraphQL responses match the query shape exactly — validated against the schema at runtime. REST responses offer no structural guarantees, forcing LLMs to handle arbitrary JSON shapes.", + }, +] + +export function ByTheNumbers() { + return ( +
+ By the numbers +

+ GraphQL vs REST for AI +

+

+ When AI agents interact with APIs, the protocol matters. Here's + how GraphQL compares to traditional REST APIs across the metrics that + directly impact LLM efficiency and accuracy. +

+ +
+ {stats.map(stat => ( + + ))} +
+
+ ) +} + +function StatCard({ + stat, +}: { + stat: (typeof stats)[number] +}) { + const [animated, setAnimated] = useState(false) + const [gqlCount, setGqlCount] = useState(0) + const [restCount, setRestCount] = useState(0) + const ref = useRef(null) + + useEffect(() => { + const el = ref.current + if (!el) return + + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setAnimated(true) + observer.disconnect() + } + }, + { threshold: 0.3 }, + ) + + observer.observe(el) + return () => observer.disconnect() + }, []) + + useEffect(() => { + if (!animated) return + + const gqlTarget = + stat.graphQL === "90%" ? 90 : stat.graphQL === "0" ? 0 : stat.graphQL === "100%" ? 100 : parseInt(stat.graphQL) || 1 + const restTarget = + stat.rest === "10× more" ? 10 : stat.rest === "3–7" ? 5 : stat.rest === "manual" ? 1 : stat.rest === "ad-hoc" ? 0 : parseInt(stat.rest) || 0 + + const gqlMax = Math.max(gqlTarget, 1) + const restMax = Math.max(restTarget, 1) + + const duration = 1200 + const start = performance.now() + + function tick(now: number) { + const elapsed = now - start + const progress = Math.min(elapsed / duration, 1) + const eased = 1 - Math.pow(1 - progress, 3) + + setGqlCount(Math.round(gqlTarget * eased)) + setRestCount(Math.round(restMax * eased)) + + if (progress < 1) requestAnimationFrame(tick) + } + + requestAnimationFrame(tick) + }, [animated, stat.graphQL, stat.rest]) + + return ( +
+

{stat.label}

+ +
+ {/* GraphQL side */} +
+

+ GraphQL +

+

+ {animated ? gqlCount : 0} + {stat.graphQL === "90%" || stat.graphQL === "100%" ? "%" : stat.graphQL === "0" ? "" : ""} +

+

+ {stat.graphQLDesc} +

+
+ + {/* REST side */} +
+

+ REST +

+

+ {animated ? restCount : 0} + {stat.rest === "10× more" ? "×" : stat.rest === "manual" ? "" : stat.rest === "3–7" ? "" : stat.rest === "ad-hoc" ? "" : ""} +

+

+ {stat.restDesc} +

+
+
+ +
+
+
+ +

+ Why: + {stat.explanation} +

+ + {/* Advantage callout */} +
+ + + GraphQL advantage: {stat.graphQL === "90%" ? "~10× token savings" : stat.graphQL === "1" ? "single round-trip" : stat.graphQL === "0" ? "zero-config" : "guaranteed types"} + +
+
+ ) +} diff --git a/src/app/(main)/ai/components/cta-community.tsx b/src/app/(main)/ai/components/cta-community.tsx new file mode 100644 index 0000000000..5ccc812cf7 --- /dev/null +++ b/src/app/(main)/ai/components/cta-community.tsx @@ -0,0 +1,41 @@ +import { Anchor } from "@/app/conf/_design-system/anchor" +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" +import { DiscordIcon } from "@/icons" + +export function CTACommunity() { + return ( +
+
+
+

+ Shape the future of +
+ GraphQL & AI +

+

+ The GraphQL AI Working Group is open to everyone. Help define how + GraphQL powers the next generation of intelligent systems — + contribute to specs, share use cases, and collaborate with the + community. +

+
+
+ + Join the Discord + + + + AI Working Group on GitHub + + +
+
+
+ ) +} diff --git a/src/app/(main)/ai/components/hero.tsx b/src/app/(main)/ai/components/hero.tsx new file mode 100644 index 0000000000..ab389eaf58 --- /dev/null +++ b/src/app/(main)/ai/components/hero.tsx @@ -0,0 +1,193 @@ +"use client" + +import { useEffect, useRef } from "react" +import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration" +import { Button } from "@/app/conf/_design-system/button" +import { SectionLabel } from "@/app/conf/_design-system/section-label" +import CheckIcon from "@/app/conf/_design-system/pixelarticons/check.svg?svgr" +import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr" + +const highlights = [ + "Self-describing schemas let agents discover your API", + "Strong typing eliminates hallucinated API calls", + "Composable queries minimize token usage by up to 90%", +] + +export function Hero() { + return ( +
+ + +
+
+ {/* Badge */} +
+ + + + + + New: GraphQL AI Working Group + +
+ + + GraphQL + AI + + +

+ The API protocol +
+ for intelligent systems +

+ +

+ When AI agents need to interact with APIs, GraphQL's + self-describing schemas, strong typing, and composable queries + make it the natural choice. No hand-written tool definitions. + No token-wasting REST payloads. Just structured, predictable data. +

+ +
    + {highlights.map((item, index) => ( +
  • + + {item} +
  • + ))} +
+ +
+ + + +
+
+
+
+ ) +} + +/* ───────────────────────────────────────────── + Subtle animated schema grid in background + ───────────────────────────────────────────── */ + +function SchemaGrid() { + const canvasRef = useRef(null) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext("2d") + if (!ctx) return + + let animId: number + let time = 0 + const nodes: { x: number; y: number; r: number; vx: number; vy: number }[] = [] + + function resize() { + if (!canvas) return + canvas.width = canvas.offsetWidth * (window.devicePixelRatio || 1) + canvas.height = canvas.offsetHeight * (window.devicePixelRatio || 1) + ctx!.setTransform(1, 0, 0, 1, 0, 0) + ctx!.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1) + } + + function init() { + resize() + const w = canvas!.offsetWidth + const h = canvas!.offsetHeight + nodes.length = 0 + for (let i = 0; i < 30; i++) { + nodes.push({ + x: Math.random() * w, + y: Math.random() * h, + r: Math.random() * 2 + 1, + vx: (Math.random() - 0.5) * 0.3, + vy: (Math.random() - 0.5) * 0.3, + }) + } + } + + function draw() { + if (!canvas || !ctx) return + const w = canvas.offsetWidth + const h = canvas.offsetHeight + + ctx.clearRect(0, 0, w, h) + time += 0.005 + + // Update & draw nodes + for (const n of nodes) { + n.x += n.vx + n.y += n.vy + Math.sin(time + n.x * 0.02) * 0.1 + if (n.x < 0) n.x = w + if (n.x > w) n.x = 0 + if (n.y < 0) n.y = h + if (n.y > h) n.y = 0 + } + + // Draw connections + const maxDist = 150 + for (let i = 0; i < nodes.length; i++) { + for (let j = i + 1; j < nodes.length; j++) { + const dx = nodes[i].x - nodes[j].x + const dy = nodes[i].y - nodes[j].y + const dist = Math.sqrt(dx * dx + dy * dy) + if (dist < maxDist) { + ctx.beginPath() + ctx.moveTo(nodes[i].x, nodes[i].y) + ctx.lineTo(nodes[j].x, nodes[j].y) + const alpha = (1 - dist / maxDist) * 0.08 + ctx.strokeStyle = `rgba(255,204,239,${alpha})` + ctx.stroke() + } + } + } + + // Draw nodes + for (const n of nodes) { + ctx.beginPath() + ctx.arc(n.x, n.y, n.r, 0, Math.PI * 2) + ctx.fillStyle = "rgba(255,204,239,0.15)" + ctx.fill() + } + + animId = requestAnimationFrame(draw) + } + + init() + draw() + window.addEventListener("resize", init) + + return () => { + cancelAnimationFrame(animId) + window.removeEventListener("resize", init) + } + }, []) + + return ( + + ) +} diff --git a/src/app/(main)/ai/components/how-it-works.tsx b/src/app/(main)/ai/components/how-it-works.tsx new file mode 100644 index 0000000000..2b27d30e86 --- /dev/null +++ b/src/app/(main)/ai/components/how-it-works.tsx @@ -0,0 +1,181 @@ +"use client" + +import { SectionLabel } from "@/app/conf/_design-system/section-label" +import SearchIcon from "@/app/conf/_design-system/pixelarticons/search.svg?svgr" +import CodeIcon from "@/app/conf/_design-system/pixelarticons/code.svg?svgr" +import PlayIcon from "@/app/conf/_design-system/pixelarticons/play.svg?svgr" +import CheckIcon from "@/app/conf/_design-system/pixelarticons/check.svg?svgr" +import { highlightGraphQL, highlightGraphQLSchema, highlightJSON, highlightPrompt, SYNTAX_CSS } from "./syntax-highlight" + +const steps = [ + { + number: "01", + title: "Agent receives a task", + icon: PlayIcon, + description: + 'A user gives an AI agent a natural language instruction — "Show me Q4 revenue by region." The agent needs to access business data through an API to fulfill this request.', + codeLabel: "User prompt", + code: `> Show me Q4 revenue broken down by region + for the top 5 performing product categories`, + highlight: highlightPrompt, + }, + { + number: "02", + title: "Agent introspects the API", + icon: SearchIcon, + description: + 'Using GraphQL introspection, the agent queries `__schema` and discovers the available types: `Product`, `Order`, `Region`, `RevenueMetrics`. It learns field names, arguments, and relationships automatically.', + codeLabel: "Introspection result → discovered types", + code: `type Product { + name: String! + category: Category! +} +type RevenueMetrics { + amount: Float! + region: Region! +} +type Order { + product: Product! + revenue: RevenueMetrics! +} +type Query { + orders(from: Date!, to: Date!): [Order!]! +}`, + highlight: highlightGraphQLSchema, + }, + { + number: "03", + title: "Agent composes a query", + icon: CodeIcon, + description: + "The LLM maps the user's intent to the discovered schema. It constructs a precise GraphQL query that fetches exactly the right data — revenue by region, top 5 categories, all in a single request — with no over-fetching.", + codeLabel: "AI-generated GraphQL query", + code: `{ + orders(from: "2024-10-01", to: "2024-12-31") { + product { + name + category { + name + } + } + revenue { + region { + name + } + amount + } + } +}`, + highlight: highlightGraphQL, + }, + { + number: "04", + title: "Structured response returned", + icon: CheckIcon, + description: + "The API returns typed, predictable JSON that exactly matches the query shape. The agent processes the results with confidence — every field is validated, every type is known. No parsing ambiguity, no hallucinated or missing fields.", + codeLabel: "Structured response (JSON)", + code: `{ + "orders": [{ + "product": { + "name": "Widget Pro", + "category": { + "name": "Electronics" + } + }, + "revenue": { + "region": { + "name": "North America" + }, + "amount": 45230.50 + } + }] +}`, + highlight: highlightJSON, + }, +] + +export function HowItWorks() { + return ( +
+
+ How it works +

+ From natural language +
+ to structured data +

+

+ Here's what happens when an AI agent uses a GraphQL API to + answer a real business question — from initial request to typed response. +

+ +