├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
└── aind-landing-Page
├── .gitignore
├── Logo.png
├── README.md
├── app
├── PostHogPageView.tsx
├── action
│ └── newsletter.ts
├── api
│ └── mailchimp
│ │ └── route.ts
├── catalog
│ └── page.tsx
├── favicon.ico
├── globals.css
├── layout.tsx
├── list
│ └── page.tsx
├── page.tsx
├── providers.tsx
└── whatsnew
│ └── page.tsx
├── components
├── globale
│ ├── footer
│ │ ├── cards.tsx
│ │ └── index.tsx
│ ├── globale-context.tsx
│ ├── globale-layout.tsx
│ └── header
│ │ ├── index.tsx
│ │ └── logo.tsx
└── shared
│ ├── button.tsx
│ ├── cards
│ ├── catalog-car.tsx
│ └── grid-card.tsx
│ ├── icons.tsx
│ ├── logo.tsx
│ ├── mobile-drawer.tsx
│ ├── popup-card.tsx
│ ├── search.tsx
│ ├── tab-selector.tsx
│ ├── tabel-tooltip.tsx
│ ├── table.tsx
│ ├── tags.tsx
│ └── tool-status.tsx
├── constant
└── moc-data.ts
├── eslint.config.mjs
├── next.config.ts
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public
├── discord-logo.png
├── footer-bg.png
├── footer-card-bg.png
├── github.png
├── github.svg
├── icons
│ ├── arrow-black.svg
│ ├── arrow-white.svg
│ ├── catalog-black.svg
│ ├── catalog-white.svg
│ ├── chevron-b-white.svg
│ ├── close-black.svg
│ ├── close-white.svg
│ ├── download-black.svg
│ ├── grid-black.svg
│ ├── grid-white.svg
│ ├── info-black.svg
│ ├── link-white.svg
│ ├── list-black.svg
│ ├── list-white.svg
│ ├── placeholder.svg
│ ├── plus-black.svg
│ ├── right-arrow.svg
│ ├── search-black.svg
│ └── search-white.svg
├── logo.svg
├── placeholder-logo.png
├── placeholder.png
├── social
│ ├── apple-podcast.svg
│ ├── discord.svg
│ ├── github.svg
│ ├── linkedIn.svg
│ ├── spotify.svg
│ ├── threads.svg
│ ├── x.svg
│ └── youtube.svg
├── tools-data.json
└── tools-data.yaml
├── scripts
└── convert-yaml.ts
├── tsconfig.json
├── type
└── tools-type.tsx
└── util
└── get-consistent-color.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 |
29 | # PyInstaller
30 | # Usually these files are written by a python script from a template
31 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
32 | *.manifest
33 | *.spec
34 |
35 | # Installer logs
36 | pip-log.txt
37 | pip-delete-this-directory.txt
38 |
39 | # Unit test / coverage reports
40 | htmlcov/
41 | .tox/
42 | .nox/
43 | .coverage
44 | .coverage.*
45 | .cache
46 | nosetests.xml
47 | coverage.xml
48 | *.cover
49 | *.py,cover
50 | .hypothesis/
51 | .pytest_cache/
52 | cover/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | .pybuilder/
76 | target/
77 |
78 | # Jupyter Notebook
79 | .ipynb_checkpoints
80 |
81 | # IPython
82 | profile_default/
83 | ipython_config.py
84 |
85 | # pyenv
86 | # For a library or package, you might want to ignore these files since the code is
87 | # intended to run in multiple environments; otherwise, check them in:
88 | # .python-version
89 |
90 | # pipenv
91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
94 | # install all needed dependencies.
95 | #Pipfile.lock
96 |
97 | # UV
98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99 | # This is especially recommended for binary packages to ensure reproducibility, and is more
100 | # commonly ignored for libraries.
101 | #uv.lock
102 |
103 | # poetry
104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105 | # This is especially recommended for binary packages to ensure reproducibility, and is more
106 | # commonly ignored for libraries.
107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108 | #poetry.lock
109 |
110 | # pdm
111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112 | #pdm.lock
113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114 | # in version control.
115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116 | .pdm.toml
117 | .pdm-python
118 | .pdm-build/
119 |
120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121 | __pypackages__/
122 |
123 | # Celery stuff
124 | celerybeat-schedule
125 | celerybeat.pid
126 |
127 | # SageMath parsed files
128 | *.sage.py
129 |
130 | # Environments
131 | .env
132 | .venv
133 | env/
134 | venv/
135 | ENV/
136 | env.bak/
137 | venv.bak/
138 |
139 | # Spyder project settings
140 | .spyderproject
141 | .spyproject
142 |
143 | # Rope project settings
144 | .ropeproject
145 |
146 | # mkdocs documentation
147 | /site
148 |
149 | # mypy
150 | .mypy_cache/
151 | .dmypy.json
152 | dmypy.json
153 |
154 | # Pyre type checker
155 | .pyre/
156 |
157 | # pytype static type analyzer
158 | .pytype/
159 |
160 | # Cython debug symbols
161 | cython_debug/
162 |
163 | # PyCharm
164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166 | # and can be added to the global gitignore or merged into this file. For a more nuclear
167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168 | #.idea/
169 |
170 | # PyPI configuration file
171 | .pypirc
172 | .gitok
173 | .gitsigned
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the AI Native Dev Landscape
2 |
3 | ## Adding a new tool
4 |
5 | To add a new tool to the landscape, you can follow these steps:
6 |
7 | 1. Fork the repository
8 | 2. Add or update the tool under `aind-landing-Page/public/tools-data.yaml` (the json one is autogenerated on deploy)
9 | 3. Create a pull request
10 |
11 | ## Alternate workflow
12 | ### Official vendor verification
13 | - If you are a vendor and want to get your tool verified or change some of the tools information, please fill in this [Official Tool Vendor - Google Form](https://forms.gle/6NV7g8UvAq4py8KK8).
14 |
15 | - If contributing via a PR is not your preferred method, you can also open up a Github Issue or send us an email at [hello@ainativedev.io](mailto:hello@ainativedev.io)
16 |
17 | ## Add or update a new domain or category
18 |
19 | To add a new domain or category to the landscape, you can follow these steps:
20 |
21 | 1. Open a Github issue for discussion
22 | 2. Once the discussion is settled, create a pull request to add the new domain or category to the landscape. Make sure it has a consistent `level` with the rest of the landscape.
23 |
24 | ## Guidelines
25 | - We tend to avoid having single tool categories, so if you are adding a new category, make sure it has more than 3 tools associated with it.
26 | - We tend to avoid putting tools in multiple categories, so if a tool is a good fit for multiple categories, try to pick the most specific one.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Tessl
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AI Native Dev Landscape - Data
2 | ## Introduction
3 | The AI Native Dev Landscape is a project by [AI Native Dev](https://ainativedev.io) to help developers keep track of the AI tools and technologies that are available. It is a community curated list and we welcome contributions.
4 |
5 | This repository contains the data that powers the [https://landscape.ainativedev.io](https://landscape.ainativedev.io) website.
6 |
7 | ## Terminology
8 | - The whole dataset is referred to as `Landscape`.
9 | - The landscape consists of different `Domains`.
10 | - Each domain has multiple `Categories`.
11 | - And under categories we have `Tools`
12 |
13 | ## Contributing to the Landscape
14 | ### Data
15 | We welcome contributions to the data. Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information.
16 |
17 | ### Website
18 | We also welcome contributions to the website. See the [README.md](aind-landing-Page/README.md) file for more information.
19 |
20 | ## YAML Structure
21 | ### Domain
22 | ```yaml
23 | domain:
24 | name: string # Name of the domain
25 | description: string # Description of the domain
26 | level: integer # Level of the domain, this is used for display purposes, the lowest level has the highest priority on the landscape map
27 | ```
28 | ### Category
29 | ```yaml
30 | category:
31 | name: string # Name of the category
32 | description: string # Description of the category
33 | level: integer # Level of the category, this is used for display purposes, the lowest level has the highest priority on the landscape map
34 | ```
35 |
36 | ### Tool
37 | ```yaml
38 | tool:
39 | name: string # Name of the tool
40 | description: string # Description of the tool
41 | icon_url: string # URL to the tool's icon
42 | website_url: string # URL to the tool's website
43 | tags: list[string] # Array of tags
44 | date_added: string # Date when the tool was added to the landscape DD/MM/YYYY
45 | oss: boolean # Whether the tool is open source
46 | verified: boolean # Whether the tool is vendor verified
47 | beta: boolean # Whether the tool is in beta (if false the tool is considered GA)
48 | popular: boolean # Indicates the tool being popular resulting it being sorted first in a category
49 | ```
50 |
51 |
52 | ## License
53 | - The code in this project is licensed under the [MIT License](LICENSE).
54 | - The data is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa].
55 |
56 | [![CC BY-NC-SA 4.0][cc-by-nc-sa-image]][cc-by-nc-sa]
57 |
58 | [cc-by-nc-sa]: http://creativecommons.org/licenses/by-nc-sa/4.0/
59 | [cc-by-nc-sa-image]: https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png
60 | [cc-by-nc-sa-shield]: https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg
61 |
62 |
63 | ## Contributors
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/aind-landing-Page/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
43 | .DS_Store
44 |
45 |
--------------------------------------------------------------------------------
/aind-landing-Page/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/Logo.png
--------------------------------------------------------------------------------
/aind-landing-Page/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
37 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/PostHogPageView.tsx:
--------------------------------------------------------------------------------
1 | // app/PostHogPageView.tsx
2 | 'use client'
3 |
4 | import { usePathname, useSearchParams } from "next/navigation"
5 | import { useEffect, Suspense } from "react"
6 | import { usePostHog } from 'posthog-js/react'
7 |
8 | function PostHogPageView() : null {
9 | const pathname = usePathname()
10 | const searchParams = useSearchParams()
11 | const posthog = usePostHog()
12 |
13 | // Track pageviews
14 | useEffect(() => {
15 | if (pathname && posthog) {
16 | let url = window.origin + pathname
17 | if (searchParams.toString()) {
18 | url = url + `?${searchParams.toString()}`
19 | }
20 |
21 | posthog.capture('$pageview', { '$current_url': url })
22 | }
23 | }, [pathname, searchParams, posthog])
24 |
25 | return null
26 | }
27 |
28 | // Wrap this in Suspense to avoid the `useSearchParams` usage above
29 | // from de-opting the whole app into client-side rendering
30 | // See: https://nextjs.org/docs/messages/deopted-into-client-rendering
31 | export default function SuspendedPostHogPageView() {
32 | return
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/aind-landing-Page/app/action/newsletter.ts:
--------------------------------------------------------------------------------
1 | "use server";
2 |
3 | import mailchimp from "@mailchimp/mailchimp_marketing";
4 |
5 | // Initialize Mailchimp with your API key and server prefix
6 | mailchimp.setConfig({
7 | apiKey: process.env.MAILCHIMP_API_KEY,
8 | server: process.env.MAILCHIMP_SERVER_PREFIX, // e.g., "us1"
9 | });
10 |
11 | interface MailchimpError extends Error {
12 | response?: {
13 | body: {
14 | title: string;
15 | detail: string;
16 | };
17 | };
18 | }
19 |
20 | export async function subscribeToNewsletter(formData: FormData) {
21 | const email = formData.get("email") as string;
22 |
23 | if (!email) {
24 | return {
25 | success: false,
26 | message: "Email is required",
27 | };
28 | }
29 |
30 | try {
31 | // Add member to list
32 | await mailchimp.lists.addListMember(
33 | process.env.MAILCHIMP_AUDIENCE_ID as string,
34 | {
35 | email_address: email,
36 | status: "subscribed",
37 | }
38 | );
39 | console.log("Success! You are now subscribed.");
40 | return {
41 | success: true,
42 | message: "Success! You are now subscribed.",
43 | };
44 | } catch (error: unknown) {
45 | const mailchimpError = error as MailchimpError;
46 | if (mailchimpError.response?.body) {
47 | const { title, detail } = mailchimpError.response.body;
48 |
49 | if (title && title.includes("Member Exists")) {
50 | return {
51 | success: false,
52 | message: "You are already subscribed to our newsletter.",
53 | };
54 | }
55 |
56 | return {
57 | success: false,
58 | message:
59 | detail || title || "An error occurred with the subscription service.",
60 | };
61 | }
62 |
63 | return {
64 | success: false,
65 | message: "An error occurred. Please try again later.",
66 | };
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/api/mailchimp/route.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import mailchimp from "@mailchimp/mailchimp_marketing";
3 |
4 | interface MailchimpError extends Error {
5 | response?: {
6 | body: {
7 | title: string;
8 | detail: string;
9 | status: number;
10 | };
11 | };
12 | }
13 |
14 | mailchimp.setConfig({
15 | apiKey: process.env.MAILCHIMP_API_KEY,
16 | server: process.env.MAILCHIMP_SERVER_PREFIX,
17 | });
18 |
19 | export async function POST(request: Request) {
20 | try {
21 | const { email } = await request.json();
22 |
23 | if (!email) {
24 | return NextResponse.json(
25 | { message: "Email is required" },
26 | { status: 400 }
27 | );
28 | }
29 |
30 | // Add member to list
31 | await mailchimp.lists.addListMember(
32 | process.env.MAILCHIMP_AUDIENCE_ID as string,
33 | {
34 | email_address: email,
35 | status: "subscribed",
36 | }
37 | );
38 |
39 | return NextResponse.json(
40 | { message: "Success! You are now subscribed." },
41 | { status: 201 }
42 | );
43 | } catch (error: unknown) {
44 | const mailchimpError = error as MailchimpError;
45 | console.log(mailchimpError);
46 |
47 | if (mailchimpError.response?.body) {
48 | const { title, detail, status } = mailchimpError.response.body;
49 |
50 | if (status === 400 && title.includes("Member Exists")) {
51 | return NextResponse.json(
52 | { message: "You are already subscribed to our newsletter." },
53 | { status: 400 }
54 | );
55 | }
56 |
57 | return NextResponse.json(
58 | { message: detail || title },
59 | { status: status || 500 }
60 | );
61 | }
62 |
63 | return NextResponse.json(
64 | { message: "An error occurred. Please try again later." },
65 | { status: 500 }
66 | );
67 | }
68 | }
--------------------------------------------------------------------------------
/aind-landing-Page/app/catalog/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import CatalogCard from "@/components/shared/cards/catalog-car";
3 | import { useGlobaleContext } from "@/components/globale/globale-context";
4 |
5 | export default function Catalog() {
6 | const { toolsData, activeTags } = useGlobaleContext();
7 |
8 | return (
9 | <>
10 | {toolsData?.domains && toolsData?.domains.length > 0 ? (
11 |
12 | {(() => {
13 | // Flatten all tools from all domains and categories
14 | const allTools = toolsData.domains.flatMap((domain) =>
15 | domain.categories.flatMap((category) =>
16 | category.tools.map((tool) => ({
17 | ...tool,
18 | domainName: domain.name,
19 | categoryName: category.name,
20 | }))
21 | )
22 | );
23 |
24 | // Filter and sort all tools together
25 | const filteredTools = activeTags.includes("all")
26 | ? allTools
27 | : allTools.filter((tool) =>
28 | tool.tags?.some((tag) => activeTags.includes(tag))
29 | );
30 |
31 | const sortedTools = [...filteredTools].sort((a, b) =>
32 | a.popular === b.popular ? 0 : a.popular ? -1 : 1
33 | );
34 |
35 | return sortedTools.map((tool) => (
36 |
42 | ));
43 | })()}
44 |
45 | ) : (
46 | toolsData?.domains.length === 0 && (
47 |
48 | No tools found
49 |
50 | )
51 | )}
52 | >
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/app/favicon.ico
--------------------------------------------------------------------------------
/aind-landing-Page/app/globals.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @theme inline {
4 | --color-background: #F4EEE2;
5 | --color-foreground: #000000;
6 | --max-width: 1366px;
7 |
8 | --border-primary: #C9C3B9;
9 | --border-secondary: #000000;
10 |
11 | --font-inter: var(--font-inter);
12 | --font-dm-mono: var(--font-dm-mono);
13 | --font-instrument-sans: var(--font-instrument-sans);
14 | --font-instrument-serif: var(--font-instrument-serif);
15 |
16 | --animate-mobile-slidein: mobile-slidein 500ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
17 | --animate-mobile-slideout: mobile-slideout 300ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
18 | --animate-header-slidein: header-slidein 500ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
19 | --animate-header-slideout: header-slideout 300ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
20 |
21 | @keyframes mobile-slidein {
22 | 0% {opacity: 0; transform: translateY(400px);}
23 | 100% {opacity: 1; transform: translateY(0);}
24 | }
25 |
26 | @keyframes mobile-slideout {
27 | 0% {opacity: 1; transform: translateY(0);}
28 | 100% {opacity: 0; transform: translateY(200px);}
29 | }
30 |
31 | @keyframes header-slidein {
32 | 0% { height: 0;}
33 | 100% { height: 100vh;}
34 | }
35 |
36 | @keyframes header-slideout {
37 | 0% {opacity: 1; height: 100vh;}
38 | 100% {opacity: 0; height: 0;}
39 | }
40 |
41 | }
42 |
43 |
44 | /* typography utilities */
45 | @utility menuItem {
46 | @apply font-instrument-sans text-sm font-normal leading-[130%];
47 | }
48 |
49 | @utility heading {
50 | @apply font-instrument-serif text-[64px] font-normal leading-[110%] tracking-[-1.28px];
51 | }
52 |
53 | @utility heading-2xl {
54 | @apply font-instrument-sans text-[30px] font-normal leading-[110%] tracking-[-0.6px];
55 | }
56 |
57 | @utility heading-xl {
58 | @apply font-inter text-[20px] font-normal leading-[110%] tracking-[-0.4px];
59 | }
60 |
61 | @utility body {
62 | @apply font-dm-mono font-normal leading-[150%];
63 | }
64 |
65 | @utility body-sm {
66 | @apply font-dm-mono text-sm font-normal leading-[150%];
67 | }
68 |
69 | @utility label {
70 | @apply font-dm-mono text-sm font-normal leading-[130%] ;
71 | }
72 |
73 | @utility label-sm {
74 | @apply font-instrument-sans text-sm font-normal leading-[130%];
75 | }
76 |
77 | @utility body-xs {
78 | @apply text-xs leading-[120%] tracking-[0.1em];
79 | }
80 |
81 |
82 |
83 | @utility body-xxs {
84 | @apply font-dm-mono text-xs leading-[130%] tracking-[1.1px] uppercase;
85 | }
86 | @utility body-lg {
87 | @apply font-inter text-base leading-[150%];
88 | }
89 |
90 |
91 |
92 | html {
93 | scroll-behavior: smooth;
94 | }
95 |
96 | body {
97 | overscroll-behavior: none;
98 | background: var(--color-background);
99 | color: var(--color-foreground);
100 | }
101 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import {
3 | DM_Mono,
4 | Inter,
5 | Instrument_Sans,
6 | Instrument_Serif,
7 | } from "next/font/google";
8 | import "./globals.css";
9 | import Header from "@/components/globale/header";
10 | import Footer from "@/components/globale/footer";
11 | import GlobalLayout from "@/components/globale/globale-layout";
12 | import { PostHogProvider } from './providers';
13 |
14 | const inter = Inter({
15 | variable: "--font-inter",
16 | subsets: ["latin"],
17 | });
18 |
19 | const dmMono = DM_Mono({
20 | variable: "--font-dm-mono",
21 | subsets: ["latin"],
22 | weight: ["300", "400", "500"],
23 | });
24 |
25 | const instrumentSans = Instrument_Sans({
26 | variable: "--font-instrument-sans",
27 | subsets: ["latin"],
28 | weight: ["400", "500", "600", "700"],
29 | });
30 |
31 | const instrumentSerif = Instrument_Serif({
32 | variable: "--font-instrument-serif",
33 | subsets: ["latin"],
34 | weight: ["400"],
35 | });
36 |
37 | export const metadata: Metadata = {
38 | title: "AI Native Developer Tools Landscape",
39 | description: "Your Guide to the AI Development Ecosystem",
40 | };
41 |
42 | export default function RootLayout({
43 | children,
44 | }: Readonly<{
45 | children: React.ReactNode;
46 | }>) {
47 | return (
48 |
49 |
52 |
53 |
54 | {children}
55 |
56 |
57 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/list/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useGlobaleContext } from "@/components/globale/globale-context";
4 |
5 | import {
6 | Table,
7 | TableBody,
8 | TableCaption,
9 | TableCell,
10 | TableHead,
11 | TableHeader,
12 | TableRow,
13 | } from "@/components/shared/table";
14 | import Link from "next/link";
15 |
16 | export default function LandscapeList() {
17 | const { toolsData, activeTags } = useGlobaleContext();
18 |
19 | return (
20 |
21 |
22 | {toolsData?.domains.length === 0 && (
23 | No tools found
24 | )}
25 |
26 |
27 |
28 | Name
29 |
30 | Description
31 | Domain
32 | Category
33 | Url
34 | {/* TODO: uncomment when you add tool status back */}
35 | {/* */}
36 |
37 |
38 |
39 |
40 | {(() => {
41 | const allTools =
42 | toolsData?.domains.flatMap((domain) =>
43 | domain.categories.flatMap((category) =>
44 | category.tools.map((tool) => ({
45 | ...tool,
46 | domainName: domain.name,
47 | categoryName: category.name,
48 | }))
49 | )
50 | ) || [];
51 |
52 | const filteredTools = activeTags.includes("all")
53 | ? allTools
54 | : allTools.filter((tool) =>
55 | tool.tags?.some((tag) => activeTags.includes(tag))
56 | );
57 |
58 | const sortedTools = [...filteredTools].sort((a, b) =>
59 | a.popular === b.popular ? 0 : a.popular ? -1 : 1
60 | );
61 |
62 | return sortedTools.map((tool) => (
63 |
66 |
70 | {tool.name}
71 |
72 |
73 | {tool.description}
74 |
75 | {tool.domainName}
76 | {tool.categoryName}
77 |
78 |
83 | {tool.website_url
84 | .replace(/^https?:\/\//, "")
85 | .replace(/^www\./, "")
86 | .replace(/\/$/, "")}
87 |
88 |
89 | {/* TODO: uncomment when you add tool status back */}
90 | {/*
91 |
92 | */}
93 |
94 | ));
95 | })()}
96 |
97 |
98 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import GridCard from "@/components/shared/cards/grid-card";
4 | import { useGlobaleContext } from "@/components/globale/globale-context";
5 |
6 | export default function Home() {
7 | const { toolsData, activeTags } = useGlobaleContext();
8 | return (
9 |
10 | {toolsData?.domains
11 | .sort((a, b) => a.level - b.level)
12 | .map((item) => {
13 | const hasMatchingTools = item.categories.some((category) => {
14 | const filteredTools = activeTags.includes("all")
15 | ? category.tools
16 | : category.tools.filter((tool) =>
17 | tool.tags?.some((tag) => activeTags.includes(tag))
18 | );
19 | return filteredTools.length > 0;
20 | });
21 |
22 | if (item.categories.length === 0 || !hasMatchingTools) return null;
23 |
24 | return (
25 |
31 | );
32 | })}
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/aind-landing-Page/app/providers.tsx:
--------------------------------------------------------------------------------
1 | // app/providers.tsx
2 | 'use client'
3 |
4 | import posthog from 'posthog-js'
5 | import { PostHogProvider as PHProvider } from 'posthog-js/react'
6 | import PostHogPageView from "./PostHogPageView"
7 |
8 | import { useEffect } from 'react'
9 |
10 | export function PostHogProvider({ children }: { children: React.ReactNode }) {
11 | useEffect(() => {
12 | posthog.init("phc_S8tHJaPxa5ORnP0RVyyTaJHeCcft6XMqfjx9ZlJWvkO", {
13 | api_host: "https://us.i.posthog.com",
14 | capture_pageview: false, // Disable automatic pageview capture, as we capture manually,
15 | capture_pageleave: true, // Enable pageleave capture
16 | person_profiles: 'identified_only', // or 'always' to create profiles for anonymous users as well
17 | })
18 | }, [])
19 |
20 | return (
21 |
22 |
23 | {children}
24 |
25 | )
26 | }
--------------------------------------------------------------------------------
/aind-landing-Page/app/whatsnew/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Suspense } from "react";
4 | import { useGlobaleContext } from "@/components/globale/globale-context";
5 | import CatalogCard from "@/components/shared/cards/catalog-car";
6 | import { useSearchParams } from "next/navigation";
7 |
8 | const getSinceDate = (searchParams: URLSearchParams) => {
9 | const rawSinceDate = searchParams.get("since");
10 | const sinceWeeks = rawSinceDate?.includes("w") ? parseInt(rawSinceDate.split("w")[0]) : null;
11 | const sinceDays = rawSinceDate?.includes("d") ? parseInt(rawSinceDate.split("d")[0]) : null;
12 |
13 | let sinceDate;
14 | if (sinceWeeks) {
15 | sinceDate = new Date(Date.now() - sinceWeeks * 7 * 24 * 60 * 60 * 1000);
16 | } else if (sinceDays) {
17 | sinceDate = new Date(Date.now() - sinceDays * 24 * 60 * 60 * 1000);
18 | } else {
19 | sinceDate = new Date(Date.now());
20 | }
21 | return sinceDate;
22 | };
23 |
24 | function WhatsNewContent() {
25 | const { toolsData } = useGlobaleContext();
26 | const searchParams = useSearchParams();
27 |
28 | const sinceDate = getSinceDate(searchParams);
29 | const sinceFormat = searchParams.get("format") || "html";
30 |
31 | // Flatten all tools from all domains and categories
32 | const allTools = toolsData?.domains.flatMap((domain) =>
33 | domain.categories.flatMap((category) =>
34 | category.tools.map((tool) => ({
35 | ...tool,
36 | domainName: domain.name,
37 | categoryName: category.name,
38 | }))
39 | )
40 | ) || [];
41 |
42 | // Sort tools by date_added in descending order
43 | const latestTools = [...allTools]
44 | .filter((tool) => {
45 | if (!sinceDate) return true;
46 |
47 | const toolDate = new Date(tool.date_added.split("/").reverse().join("-"));
48 | const filterDate = new Date(sinceDate);
49 | return toolDate >= filterDate;
50 | })
51 | .sort((a, b) => {
52 | const dateA = new Date(a.date_added.split("/").reverse().join("-"));
53 | const dateB = new Date(b.date_added.split("/").reverse().join("-"));
54 | return dateB.getTime() - dateA.getTime();
55 | });
56 |
57 | const toolsAddedSinceDate = latestTools.filter(tool => new Date(tool.date_added.split("/").reverse().join("-")) >= sinceDate);
58 |
59 | const maxDescriptionLength = 250;
60 | if (sinceFormat === "html") {
61 | return (
62 |
63 |
64 |
What's New
65 | {sinceDate && (
66 |
67 | Showing {toolsAddedSinceDate.length} tools added since {new Date(sinceDate).toLocaleDateString()}
68 |
69 | )}
70 |
71 |
72 | {latestTools.length > 0 ? (
73 | latestTools.map((tool) => (
74 |
80 | ))
81 | ) : (
82 |
83 | No tools found for the selected date range
84 |
85 | )}
86 |
87 |
88 | );
89 | } else {
90 | return (
91 |
92 |
AI Native Dev Landscape Tools added since {new Date(sinceDate).toLocaleDateString()}: {toolsAddedSinceDate.length}
93 |
94 | {latestTools.length > 0 ? (
95 | latestTools.map((tool) => (
96 |
97 |
98 |
Description: {tool.description.trim().length > maxDescriptionLength ?
99 | tool.description.trim().replace(/\n/g, ' ').substring(0, maxDescriptionLength) + '...' :
100 | tool.description.trim().replace(/\n/g, ' ')}
101 |
102 |
Category: {tool.categoryName} - Domain: {tool.domainName} - Date Added: {tool.date_added}
103 |
104 | ))
105 | ) : (
106 | "No tools found for the selected date range"
107 | )}
108 |
109 |
110 | );
111 | }
112 | }
113 |
114 | export default function WhatsNew() {
115 | return (
116 | Loading...}>
117 |
118 |
119 | );
120 | }
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/footer/cards.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { subscribeToNewsletter } from "@/app/action/newsletter";
4 | import { Button } from "@/components/shared/button";
5 | import { cx } from "cva";
6 | import Link from "next/link";
7 | import { useActionState, useRef } from "react";
8 |
9 | export default function Cards({
10 | title,
11 | caption,
12 | description,
13 | label,
14 | variant,
15 | }: {
16 | title: string;
17 | caption: string;
18 | description: string;
19 | label: string;
20 | variant: "discord" | "newsletter";
21 | }) {
22 | const [state, action] = useActionState<
23 | {
24 | success: boolean;
25 | message: string;
26 | } | null,
27 | FormData
28 | >((_, formData) => subscribeToNewsletter(formData), null);
29 | const formRef = useRef(null);
30 |
31 | if (state?.success && formRef.current) {
32 | formRef.current.reset();
33 | }
34 | return (
35 |
47 |

52 | {variant === "discord" && (
53 |

58 | )}
59 |
60 |
{caption}
61 |
62 | {title}
63 |
64 |
{description}
65 |
66 | {variant === "newsletter" ? (
67 | state?.success ? (
68 |
69 | You're on the list! Thanks for signing up to our newsletter.
70 |
71 | ) : (
72 |
100 | )
101 | ) : (
102 |
110 | )}
111 |
112 | );
113 | }
114 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/footer/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { footerSocialLinks } from "@/constant/moc-data";
3 | import { Icon, IconName } from "@/components/shared/icons";
4 | import Link from "next/link";
5 | import Cards from "./cards";
6 |
7 | export default function Footer() {
8 | return (
9 |
10 |
11 |
18 |
25 |
26 |
27 |

32 |
33 |
34 | {footerSocialLinks.map((item) => (
35 |
36 |
37 |
38 | ))}
39 |
40 |
41 |
42 |
46 | Cookies
47 |
48 |
52 | Privacy
53 |
54 |
55 |
© 2025 AI Native Development
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/globale-context.tsx:
--------------------------------------------------------------------------------
1 | import { ToolsData } from "@/type/tools-type";
2 | import { createContext, useContext } from "react";
3 |
4 | export type GlobaleContextType = {
5 | activeTags: string[];
6 | toolsData: ToolsData | undefined;
7 | };
8 |
9 | export const GlobaleContext = createContext(
10 | undefined
11 | );
12 |
13 | export function useGlobaleContext() {
14 | const context = useContext(GlobaleContext);
15 | if (context === undefined) {
16 | throw new Error("useGlobaleContext must be used within a GlobaleProvider");
17 | }
18 | return context;
19 | }
20 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/globale-layout.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type React from "react";
4 |
5 | import { Icon } from "@/components/shared/icons";
6 | import Tags from "@/components/shared/tags";
7 | import initialToolsData from "@/public/tools-data.json";
8 | import type { ToolsData } from "@/type/tools-type";
9 | import { cx } from "cva";
10 | import Link from "next/link";
11 | import { usePathname } from "next/navigation";
12 | import { useEffect, useRef, useState } from "react";
13 | import Search from "../shared/search";
14 | import { getConsistentColor } from "@/util/get-consistent-color";
15 | import { GlobaleContext, type GlobaleContextType } from "./globale-context";
16 | import MobileDrawer from "../shared/mobile-drawer";
17 | import TabSelector from "../shared/tab-selector";
18 | import { Button } from "../shared/button";
19 |
20 | export default function GlobalLayout({
21 | children,
22 | }: {
23 | children: React.ReactNode;
24 | }) {
25 | const pathname = usePathname();
26 | const [searchTerm, setSearchTerm] = useState("");
27 | const [contentHeight, setContentHeight] = useState("100%");
28 | const tagsContainerRef = useRef(null);
29 | const [data, setData] = useState(
30 | initialToolsData as unknown as ToolsData
31 | );
32 | const [activeTags, setActiveTags] = useState(["all"]);
33 | const [isMounted, setIsMounted] = useState(false);
34 | const [isSearchOpen, setIsSearchOpen] = useState(false);
35 | const [showAllTags, setShowAllTags] = useState(true);
36 |
37 | // Client-side only code
38 | useEffect(() => {
39 | setIsMounted(true);
40 |
41 | // Get search param from URL on initial load
42 | if (typeof window !== "undefined") {
43 | const urlParams = new URLSearchParams(window.location.search);
44 | const search = urlParams.get("search") || "";
45 | setSearchTerm(search);
46 | }
47 | }, []);
48 |
49 | // Handle search term changes
50 | useEffect(() => {
51 | const fetchData = async () => {
52 | if (searchTerm && isMounted) {
53 | try {
54 | const response = await fetch("/tools-data.json");
55 | const newData = await response.json();
56 | setData(newData);
57 | } catch (error) {
58 | console.error("Error fetching data:", error);
59 | }
60 | }
61 | };
62 |
63 | fetchData();
64 | }, [searchTerm, isMounted]);
65 |
66 | useEffect(() => {
67 | if (tagsContainerRef.current) {
68 | setContentHeight(tagsContainerRef.current.scrollHeight + "px");
69 | }
70 | }, []);
71 | // Update search handler to be passed to Search component
72 | const handleSearch = (value: string) => {
73 | setSearchTerm(value);
74 | };
75 |
76 | const uniqueTags = data?.domains
77 | .flatMap((domain) => domain.categories)
78 | .flatMap((category) => category.tools)
79 | .flatMap((tool) => tool.tags || [])
80 | .filter((tag, index, self) => self.indexOf(tag) === index)
81 | .map((name) => ({
82 | id: name,
83 | name: name,
84 | color: getConsistentColor(name),
85 | }));
86 |
87 | const tagsWithAll = [
88 | {
89 | id: "all",
90 | name: "Show all",
91 | color: undefined,
92 | },
93 | ...(uniqueTags || []),
94 | ];
95 |
96 | const handleTagClick = (tagId: string) => {
97 | if (tagId === "all") {
98 | setActiveTags(["all"]);
99 | return;
100 | }
101 |
102 | setActiveTags((prev) => {
103 | const withoutAll = prev.filter((tag) => tag !== "all");
104 |
105 | if (withoutAll.includes(tagId)) {
106 | const newTags = withoutAll.filter((tag) => tag !== tagId);
107 | return newTags.length === 0 ? ["all"] : newTags;
108 | } else {
109 | return [...withoutAll, tagId];
110 | }
111 | });
112 | };
113 | const filteredData = searchTerm
114 | ? {
115 | ...data,
116 | domains:
117 | data?.domains
118 | .map((domain) => ({
119 | ...domain,
120 | categories: domain.categories
121 | .map((category) => ({
122 | ...category,
123 | tools: category.tools.filter(
124 | (tool) =>
125 | tool.name
126 | .toLowerCase()
127 | .includes(searchTerm.toLowerCase()) ||
128 | tool.description
129 | ?.toLowerCase()
130 | .includes(searchTerm.toLowerCase()) ||
131 | tool.tags?.some((tag) =>
132 | tag.toLowerCase().includes(searchTerm.toLowerCase())
133 | )
134 | ),
135 | }))
136 | .filter((category) => category.tools.length > 0),
137 | }))
138 | .filter((domain) => domain.categories.length > 0) || [],
139 | }
140 | : data;
141 |
142 | const contextValue: GlobaleContextType = {
143 | activeTags,
144 | toolsData: filteredData,
145 | };
146 |
147 | const numberActiveTags = activeTags.length;
148 | const numberTools = data?.domains.flatMap((domain) =>
149 | domain.categories.flatMap((category) => category.tools)
150 | ).length;
151 |
152 | const lastUpdated = (() => {
153 | const allTools = initialToolsData.domains
154 | .flatMap((domain) => domain.categories)
155 | .flatMap((category) => category.tools);
156 |
157 | const latestDate = allTools
158 | .map((tool) => {
159 | const [day, month, year] = tool.date_added.split("/").map(Number);
160 | return new Date(year, month - 1, day); // Correct date parsing
161 | })
162 | .reduce(
163 | (latest, current) =>
164 | current.getTime() > latest.getTime() ? current : latest,
165 | new Date(0)
166 | );
167 |
168 | return {
169 | weekday: latestDate.toLocaleDateString("en-US", { weekday: "long" }),
170 | date: `${latestDate.getDate()} ${latestDate.toLocaleDateString("en-US", {
171 | month: "long",
172 | })}`,
173 | };
174 | })();
175 | return (
176 |
177 |
178 |
179 |
Landscape
180 |
181 | Your Guide to the AI Development Ecosystem
182 |
183 |
184 |
185 |
186 |
Stats
187 |
{numberTools} tools
188 |
202 |
203 |
204 |
Last updated
205 |
206 | {lastUpdated.weekday}
207 |
208 | {lastUpdated.date}
209 |
210 |
211 |
212 |
213 |
214 |
215 |
222 |
236 | {[...tagsWithAll]
237 | .sort((a, b) =>
238 | a.id === "all"
239 | ? -1
240 | : b.id === "all"
241 | ? 1
242 | : a.name.localeCompare(b.name)
243 | )
244 | .map((tag) => (
245 | handleTagClick(tag.id.toString())}
251 | />
252 | ))}
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
268 | {tagsWithAll.map((tag) => (
269 | handleTagClick(tag.id.toString())}
275 | />
276 | ))}
277 |
278 |
284 |
285 |
286 | {children}
287 |
288 |
289 | );
290 | }
291 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/header/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState } from "react";
3 | import Logo from "./logo";
4 | import { navLinks } from "../../../constant/moc-data";
5 | import Link from "next/link";
6 | import { cx } from "cva";
7 | import { VisuallyHidden } from "radix-ui";
8 | import * as Dialog from "@radix-ui/react-dialog";
9 | import { RemoveScroll } from "react-remove-scroll";
10 |
11 | export default function Header() {
12 | const [openHamburger, setOpenHamburger] = useState(false);
13 | return (
14 |
15 |
16 |
17 |
18 | {navLinks.map((link) => (
19 | -
20 | {link.label}
21 |
22 | ))}
23 |
24 |
25 |
26 |
27 |
28 |
29 | setOpenHamburger(!openHamburger)}
32 | >
33 |
39 |
45 |
46 |
47 |
48 |
49 |
53 |
54 | Menu
55 |
56 |
57 | {navLinks.map((link) => (
58 | setOpenHamburger(false)}
63 | >
64 | {link.label}
65 |
66 | ))}
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/globale/header/logo.tsx:
--------------------------------------------------------------------------------
1 | export default function Logo() {
2 | return
;
3 | }
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/button.tsx:
--------------------------------------------------------------------------------
1 | import { cva, type VariantProps } from "cva";
2 | import { forwardRef } from "react";
3 | import { Icon } from "./icons";
4 |
5 | const buttonVariants = cva(
6 | "flex items-center gap-2 font-medium menuItem font-instrument-sans cursor-pointer outline-none border border-black justify-center items-center rounded-full",
7 | {
8 | variants: {
9 | variant: {
10 | primary: "bg-black text-white",
11 | secondary: "bg-transparent text-black ",
12 | },
13 | fullWidth: {
14 | true: "w-full",
15 | },
16 | size: {
17 | small: "px-2.25 py-1.25 text-[11px] h-fit leading-[100%]",
18 | medium: "px-4 py-2.75 ",
19 | large: "px-6 py-3.75 ",
20 | },
21 | },
22 | defaultVariants: {
23 | variant: "primary",
24 | size: "medium",
25 | },
26 | }
27 | );
28 |
29 | export type ButtonProps = React.ButtonHTMLAttributes &
30 | VariantProps & {
31 | linkIcon?: boolean;
32 | arrow?: "black" | "white" | null;
33 | };
34 |
35 | export const Button = forwardRef(
36 | (
37 | {
38 | className,
39 | variant,
40 | fullWidth,
41 | size,
42 | children,
43 | arrow = null,
44 | linkIcon,
45 | ...props
46 | },
47 | ref
48 | ) => {
49 | return (
50 |
64 | );
65 | }
66 | );
67 |
68 | Button.displayName = "Button";
69 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/cards/catalog-car.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Button } from "../button";
3 | import { Tool } from "@/type/tools-type";
4 | import Link from "next/link";
5 |
6 | import ToolStatus from "../tool-status";
7 | import { PopupCard } from "../popup-card";
8 | import Logo from "../logo";
9 |
10 | export default function CatalogCard({
11 | tool,
12 | category,
13 | domain,
14 | }: {
15 | tool: Tool;
16 | category: string;
17 | domain: string;
18 | }) {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
{tool.name}
35 |
36 |
37 |
38 |
52 |
53 |
54 |
55 |
56 |
Domain
57 |
{domain}
58 |
59 |
60 |
Category
61 |
{category}
62 |
63 |
64 |
Date added
65 |
{tool.date_added}
66 |
67 |
68 |
69 |
70 |
Description
71 |
72 | {tool.description.split("\\n\\n").map((paragraph, index) => (
73 |
74 | {paragraph}
75 | {index < tool.description.split("\\n\\n").length - 1 && (
76 |
77 | )}
78 |
79 | ))}
80 |
81 |
82 | {tool.tags && (
83 |
84 |
Tags
85 |
86 | {tool.tags?.map((tag, index) => (
87 |
88 | {tag}
89 |
90 | ))}
91 |
92 |
93 | )}
94 |
95 |
Url
96 |
101 | {tool.website_url}
102 |
103 |
104 |
105 |
111 | Suggest Edits
112 |
113 |
114 |
115 |
121 |
126 |
127 |
128 | );
129 | }
130 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/cards/grid-card.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Icon } from "../icons";
4 | import { ToolsData } from "@/type/tools-type";
5 | import { cx } from "cva";
6 | import { useState } from "react";
7 | import { getConsistentColor } from "@/util/get-consistent-color";
8 | import { PopupCard } from "../popup-card";
9 | import Logo from "../logo";
10 | import { TableTooltip } from "../tabel-tooltip";
11 | import { useGlobaleContext } from "@/components/globale/globale-context";
12 |
13 | export default function GridCard({
14 | categories,
15 | title,
16 | description,
17 | }: {
18 | categories: ToolsData["domains"][0]["categories"];
19 | title: string;
20 | description: string;
21 | }) {
22 | const [expanded, setExpanded] = useState(false);
23 | const { activeTags } = useGlobaleContext();
24 |
25 | return (
26 |
27 |
28 |
34 |
{title}
35 |
36 |
37 |
38 |
39 |
40 | {categories
41 | .sort((a, b) => a.level - b.level)
42 | .map((category) => {
43 | if (category.tools.length === 0) return null;
44 | const filteredTools = activeTags.includes("all")
45 | ? category.tools
46 | : category.tools.filter((tool) =>
47 | tool.tags?.some((tag) => activeTags.includes(tag))
48 | );
49 | if (filteredTools.length === 0) return null;
50 | return {
51 | category,
52 | filteredTools,
53 | } as const;
54 | })
55 | .filter(
56 | (
57 | item
58 | ): item is {
59 | category: (typeof categories)[0];
60 | filteredTools: (typeof categories)[0]["tools"];
61 | } => item !== null
62 | )
63 | .map(({ category, filteredTools }, visibleIndex) => {
64 | const filtredToolsLength = filteredTools.length;
65 | return (
66 |
=
74 | categories.length - (categories.length % 3 || 3),
75 | }
76 | )}
77 | >
78 |
2,
83 | }
84 | )}
85 | >
86 |
{category.name}
87 |
88 | {filtredToolsLength !== category.tools.length
89 | ? `${filtredToolsLength} of ${category.tools.length}`
90 | : category.tools.length}
91 |
92 |
93 |
94 | {filteredTools
95 | .sort((a, b) => (b.popular === true ? 1 : -1))
96 | .slice(0, expanded ? category.tools.length : 11)
97 | .map((tool) => (
98 |
104 |
105 |
106 |
107 |
108 | {tool.name}
109 |
110 |
111 |
112 |
113 | ))}
114 | {category.tools.length > 30 && (
115 |
131 | )}
132 |
133 |
134 | );
135 | })}
136 |
137 |
138 | );
139 | }
140 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/icons.tsx:
--------------------------------------------------------------------------------
1 | import { cx } from "cva";
2 |
3 | const icons = {
4 | searchWhite: "/icons/search-white.svg",
5 | searchBlack: "/icons/search-black.svg",
6 | catalogWhite: "/icons/catalog-white.svg",
7 | catalogBlack: "/icons/catalog-black.svg",
8 | closeBlack: "/icons/close-black.svg",
9 | downloadBlack: "/icons/download-black.svg",
10 | infoBlack: "/icons/info-black.svg",
11 | gridBlack: "/icons/grid-black.svg",
12 | gridWhite: "/icons/grid-white.svg",
13 | listBlack: "/icons/list-black.svg",
14 | listWhite: "/icons/list-white.svg",
15 | rightArrowBlack: "/icons/right-arrow.svg",
16 | rightArrowWhite: "/icons/right-arrow-white.svg",
17 | plusBlack: "/icons/plus-black.svg",
18 | arrowWhite: "/icons/arrow-white.svg",
19 | linkWhite: "/icons/link-white.svg",
20 | arrowBlack: "/icons/arrow-black.svg",
21 | chevronBWhite: "/icons/chevron-b-white.svg",
22 | closeWhite: "/icons/close-white.svg",
23 | placeholder: "/icons/placeholder.svg",
24 | githubNew: "/github.svg",
25 |
26 | spotify: "/social/spotify.svg",
27 | instagram: "/social/instagram.svg",
28 | x: "/social/x.svg",
29 | youtube: "/social/youtube.svg",
30 | linkedIn: "/social/linkedIn.svg",
31 | applePodcast: "/social/apple-podcast.svg",
32 | discord: "/social/discord.svg",
33 | github: "/social/github.svg",
34 | threads: "/social/threads.svg",
35 | };
36 |
37 | export type IconName = keyof typeof icons;
38 |
39 | type Props = Omit, "sizes" | "src" | "srcSet"> & {
40 | name: IconName;
41 | };
42 |
43 | export function Icon({ name, alt, className, ...rest }: Props) {
44 | return (
45 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/logo.tsx:
--------------------------------------------------------------------------------
1 | import { Tool } from "@/type/tools-type";
2 | import React from "react";
3 | import { Icon } from "./icons";
4 | import Image from "next/image";
5 | import { cx } from "cva";
6 |
7 | export default function Logo({
8 | tool,
9 | size = "medium",
10 | className,
11 | }: {
12 | tool: Tool;
13 | size?: "medium" | "large";
14 | className?: string;
15 | }) {
16 | const [imageError, setImageError] = React.useState(false);
17 |
18 | return (
19 | <>
20 | {tool.icon_url && !imageError ? (
21 |
setImageError(true)}
29 | />
30 | ) : tool.oss ? (
31 |
38 | ) : (
39 |
49 | )}
50 | >
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/mobile-drawer.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { VisuallyHidden } from "radix-ui";
4 | import * as Dialog from "@radix-ui/react-dialog";
5 | import { useState } from "react";
6 | import { Icon, type IconName } from "./icons";
7 | import { Button } from "./button";
8 | import { cx } from "cva";
9 | import TabSelector from "./tab-selector";
10 | import { Tabs } from "@/constant/moc-data";
11 |
12 | export default function MobileDrawer({
13 | children,
14 | numberActiveTags,
15 | className,
16 | pathname,
17 | }: {
18 | children: React.ReactNode;
19 | numberActiveTags: number;
20 | className?: string;
21 | pathname: string;
22 | }) {
23 | const [open, setOpen] = useState(false);
24 | const tab = Tabs.find((tab) => tab.href === pathname);
25 | return (
26 |
27 |
28 |
48 |
49 |
50 |
51 |
52 |
53 | Tags selector
54 |
55 |
56 |
60 |
61 | View
62 |
63 | Categories
64 |
65 | {children}
66 |
67 |
76 |
77 |
78 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/popup-card.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as Dialog from "@radix-ui/react-dialog";
4 | import { VisuallyHidden } from "radix-ui";
5 | import { cx } from "cva";
6 | import { Icon } from "./icons";
7 | import ToolStatus from "./tool-status";
8 | import { Tool } from "@/type/tools-type";
9 | import Link from "next/link";
10 | import { Button } from "./button";
11 | import React from "react";
12 |
13 | export function PopupCard({
14 | children,
15 | tool,
16 | category,
17 | domain,
18 | }: {
19 | children: React.ReactNode;
20 | tool: Tool;
21 | category: string;
22 | domain: string;
23 | }) {
24 | return (
25 |
26 |
27 | {children}
28 |
29 |
30 |
31 |
36 |
37 | Tool information
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {tool.icon_url ? (
46 |
47 |

52 |
53 | ) : (
54 |
55 | )}
56 |
57 |
58 |
59 | {tool.name}
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Domain
68 |
{domain}
69 |
70 |
71 |
Category
72 |
{category}
73 |
74 |
75 |
Date added
76 |
{tool.date_added}
77 |
78 |
79 |
80 |
81 |
Description
82 |
"),
85 | }}
86 | className="text-xs font-dm-mono tracking-[1.1px] leading-[160%]"
87 | />
88 |
89 | {tool.tags && (
90 |
91 |
Tags
92 |
93 | {tool.tags?.map((tag, index) => (
94 |
95 | {tag}
96 |
97 | ))}
98 |
99 |
100 | )}
101 |
102 |
Url
103 |
108 | {tool.website_url}
109 |
110 |
111 |
112 |
113 |
114 |
123 |
129 | Suggest Edits
130 |
131 |
132 |
133 |
134 |
135 | );
136 | }
137 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/search.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import type React from "react";
4 | import { useState, useEffect } from "react";
5 | import { Icon } from "./icons";
6 | import { cx } from "cva";
7 |
8 | interface SearchProps {
9 | onSearch: (value: string) => void;
10 | initialValue?: string;
11 | isSearchOpen?: boolean;
12 | setIsSearchOpen?: (value: boolean) => void;
13 | }
14 |
15 | export default function Search({
16 | onSearch,
17 | initialValue = "",
18 | isSearchOpen,
19 | setIsSearchOpen,
20 | }: SearchProps) {
21 | const [searchQuery, setSearchQuery] = useState(initialValue);
22 | const [isMounted, setIsMounted] = useState(false);
23 |
24 | useEffect(() => {
25 | setIsMounted(true);
26 |
27 | if (typeof window !== "undefined") {
28 | const urlParams = new URLSearchParams(window.location.search);
29 | const search = urlParams.get("search") || "";
30 | setSearchQuery(search);
31 | }
32 | }, []);
33 |
34 | useEffect(() => {
35 | if (initialValue !== searchQuery) {
36 | setSearchQuery(initialValue);
37 | }
38 | }, [initialValue]);
39 |
40 | const handleSearch = (e: React.ChangeEvent) => {
41 | const value = e.target.value;
42 | setSearchQuery(value);
43 |
44 | onSearch(value);
45 |
46 | if (isMounted && typeof window !== "undefined") {
47 | const params = new URLSearchParams(window.location.search);
48 |
49 | if (value) {
50 | params.set("search", value);
51 | } else {
52 | params.delete("search");
53 | }
54 |
55 | const newUrl = `${window.location.pathname}?${params.toString()}`;
56 | window.history.pushState({ path: newUrl }, "", newUrl);
57 | }
58 | };
59 |
60 | return (
61 | <>
62 |
63 |
67 |
74 |
75 |
76 | {!isSearchOpen && (
77 |
83 | )}
84 |
92 |
96 |
108 |
129 |
130 |
131 | >
132 | );
133 | }
134 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/tab-selector.tsx:
--------------------------------------------------------------------------------
1 | import { Tabs } from "@/constant/moc-data";
2 | import Link from "next/link";
3 | import React from "react";
4 | import { Icon, type IconName } from "./icons";
5 | import { cx } from "cva";
6 |
7 | export default function TabSelector({
8 | pathname,
9 | variant = "desktop",
10 | }: {
11 | pathname: string;
12 | variant?: "mobile" | "desktop";
13 | }) {
14 | return (
15 |
16 | {Tabs.map((tab) => (
17 |
29 |
34 |
{tab.name}
35 |
36 | ))}
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/tabel-tooltip.tsx:
--------------------------------------------------------------------------------
1 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2 |
3 | export function TableTooltip({
4 | children,
5 | text,
6 | type = "string",
7 | }: {
8 | children: React.ReactNode;
9 | text: React.ReactNode;
10 | type?: "string" | "html";
11 | }) {
12 | return (
13 |
14 |
15 | {children}
16 |
17 | {text && (
18 |
23 | {type === "html" ? (
24 | ")
29 | : text,
30 | }}
31 | className="text-xs font-dm-mono tracking-[1.1px] leading-[160%]"
32 | />
33 | ) : (
34 | text
35 | )}
36 |
37 | )}
38 |
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cx } from "cva";
4 | import { TableTooltip } from "./tabel-tooltip";
5 |
6 | const Table = React.forwardRef<
7 | HTMLTableElement,
8 | React.HTMLAttributes
9 | >(({ className, ...props }, ref) => (
10 |
18 | ));
19 | Table.displayName = "Table";
20 |
21 | const TableHeader = React.forwardRef<
22 | HTMLTableSectionElement,
23 | React.HTMLAttributes
24 | >(({ className, ...props }, ref) => (
25 |
30 | ));
31 | TableHeader.displayName = "TableHeader";
32 |
33 | const TableBody = React.forwardRef<
34 | HTMLTableSectionElement,
35 | React.HTMLAttributes
36 | >(({ className, ...props }, ref) => (
37 |
42 | ));
43 | TableBody.displayName = "TableBody";
44 |
45 | const TableRow = React.forwardRef<
46 | HTMLTableRowElement,
47 | React.HTMLAttributes
48 | >(({ className, ...props }, ref) => (
49 |
57 | ));
58 | TableRow.displayName = "TableRow";
59 |
60 | const TableCaption = React.forwardRef<
61 | HTMLTableCaptionElement,
62 | React.HTMLAttributes
63 | >(({ className, ...props }, ref) => (
64 |
65 | ));
66 | TableCaption.displayName = "TableCaption";
67 |
68 | const TableHead = React.forwardRef<
69 | HTMLTableCellElement,
70 | React.ThHTMLAttributes
71 | >(({ className, ...props }, ref) => (
72 | |
80 | ));
81 | TableHead.displayName = "TableHead";
82 |
83 | const TableCell = React.forwardRef<
84 | HTMLTableCellElement,
85 | React.TdHTMLAttributes & {
86 | showTooltip?: boolean;
87 | type?: "string" | "html";
88 | }
89 | >(({ className, showTooltip = false, type = "string", ...props }, ref) => (
90 | <>
91 | {showTooltip ? (
92 |
93 |
98 | {props.children}
99 | |
100 |
101 | ) : (
102 |
107 | {props.children}
108 | |
109 | )}
110 | >
111 | ));
112 | TableCell.displayName = "TableCell";
113 |
114 | export {
115 | Table,
116 | TableHeader,
117 | TableBody,
118 | // TableFooter,
119 | TableCaption,
120 | TableHead,
121 | TableRow,
122 | TableCell,
123 | };
124 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/tags.tsx:
--------------------------------------------------------------------------------
1 | import { cx } from "cva";
2 |
3 | export default function Tags({
4 | name,
5 | color,
6 | active,
7 | onClick,
8 | }: {
9 | name: string;
10 | color?: string;
11 | active?: boolean;
12 | onClick?: () => void;
13 | }) {
14 | return (
15 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/aind-landing-Page/components/shared/tool-status.tsx:
--------------------------------------------------------------------------------
1 | export default function ToolStatus({ date }: { date: string }) {
2 | const isNew = new Date(date) > new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);
3 |
4 | // ignore the dates for now
5 | // if (!isNew) {
6 | if (isNew != null) {
7 | return null;
8 | }
9 |
10 | return (
11 |
12 | New
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/aind-landing-Page/constant/moc-data.ts:
--------------------------------------------------------------------------------
1 | // Header
2 | export const navLinks = [
3 | {
4 | id: 1,
5 | label: "Home",
6 | href: "https://ainativedev.io/",
7 | },
8 | {
9 | id: 2,
10 | label: "News",
11 | href: "https://ainativedev.io/news",
12 | },
13 | {
14 | id: 3,
15 | label: "Podcast",
16 | href: "https://ainativedev.io/podcast",
17 | },
18 | {
19 | id: 4,
20 | label: "About",
21 | href: "https://ainativedev.io/about",
22 | },
23 | {
24 | id: 5,
25 | label: "Landscape",
26 | href: "https://landscape.ainativedev.io/",
27 | },
28 | {
29 | id: 6,
30 | label: "AINDCon2025",
31 | href: "http://ainativedev.co/t4c",
32 | },
33 | ];
34 |
35 | // Footer Social Links
36 | export const footerSocialLinks = [
37 | {
38 | id: 1,
39 | icon: "discord",
40 | href: "https://ainativedev.co/fyf",
41 | },
42 | {
43 | id: 2,
44 | icon: "linkedIn",
45 | href: "https://ainativedev.co/e39",
46 | },
47 | {
48 | id: 3,
49 | icon: "github",
50 | href: "https://github.com/AI-Native-Dev-Community/ai-native-dev-landscape",
51 | },
52 | {
53 | id: 4,
54 | icon: "x",
55 | href: "https://ainativedev.co/756",
56 | },
57 | {
58 | id: 5,
59 | icon: "youtube",
60 | href: "https://ainativedev.co/15v",
61 | },
62 | {
63 | id: 6,
64 | icon: "applePodcast",
65 | href: "https://ainativedev.co/3wx",
66 | },
67 | {
68 | id: 7,
69 | icon: "spotify",
70 | href: "https://ainativedev.co/cii",
71 | },
72 | ];
73 |
74 | export const Tabs = [
75 | {
76 | id: "landscape",
77 | name: "Grid",
78 | href: "/",
79 | icon: "grid",
80 | },
81 | {
82 | id: "catalog",
83 | name: "Catalog",
84 | href: "/catalog",
85 | icon: "catalog",
86 | },
87 | {
88 | id: "list",
89 | name: "List",
90 | href: "/list",
91 | icon: "list",
92 | },
93 | ];
94 |
--------------------------------------------------------------------------------
/aind-landing-Page/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { dirname } from "path";
2 | import { fileURLToPath } from "url";
3 | import { FlatCompat } from "@eslint/eslintrc";
4 |
5 | const __filename = fileURLToPath(import.meta.url);
6 | const __dirname = dirname(__filename);
7 |
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | });
11 |
12 | const eslintConfig = [
13 | ...compat.extends("next/core-web-vitals", "next/typescript"),
14 | {
15 | rules: {
16 | "@next/next/no-img-element": "off"
17 | }
18 | }
19 | ];
20 |
21 | export default eslintConfig;
22 |
--------------------------------------------------------------------------------
/aind-landing-Page/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | // output: "export",
6 | };
7 |
8 | export default nextConfig;
9 |
--------------------------------------------------------------------------------
/aind-landing-Page/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ainativedev-landscape",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev --turbopack",
7 | "convert-yaml": "ts-node scripts/convert-yaml.ts",
8 | "build": "pnpm run convert-yaml && next build",
9 | "start": "next start",
10 | "lint": "next lint"
11 | },
12 | "dependencies": {
13 | "@mailchimp/mailchimp_marketing": "^3.0.80",
14 | "@radix-ui/react-dialog": "^1.1.6",
15 | "@radix-ui/react-tooltip": "^1.1.8",
16 | "cva": "npm:class-variance-authority@^0.7.1",
17 | "js-yaml": "^4.1.0",
18 | "next": "15.2.2",
19 | "posthog-js": "^1.232.7",
20 | "radix-ui": "^1.1.3",
21 | "react": "^19.0.0",
22 | "react-dom": "^19.0.0",
23 | "react-remove-scroll": "^2.6.3",
24 | "swr": "^2.3.3",
25 | "yaml": "^2.7.0"
26 | },
27 | "devDependencies": {
28 | "@eslint/eslintrc": "^3",
29 | "@tailwindcss/postcss": "^4",
30 | "@types/js-yaml": "^4.0.9",
31 | "@types/mailchimp__mailchimp_marketing": "^3.0.21",
32 | "@types/node": "^20.17.24",
33 | "@types/react": "^19",
34 | "@types/react-dom": "^19",
35 | "eslint": "^9",
36 | "eslint-config-next": "15.2.2",
37 | "serve": "^14.2.4",
38 | "tailwindcss": "^4",
39 | "ts-node": "^10.9.2",
40 | "typescript": "^5"
41 | },
42 | "description": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).",
43 | "main": "index.js",
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/AI-Native-Dev-Community/ai-native-dev-landscape.git"
47 | },
48 | "keywords": [],
49 | "author": "",
50 | "license": "ISC",
51 | "bugs": {
52 | "url": "https://github.com/AI-Native-Dev-Community/ai-native-dev-landscape/issues"
53 | },
54 | "homepage": "https://landscape.ainativedev.io"
55 | }
56 |
--------------------------------------------------------------------------------
/aind-landing-Page/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | const config = {
2 | plugins: ["@tailwindcss/postcss"],
3 | };
4 |
5 | export default config;
6 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/discord-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/discord-logo.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/footer-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/footer-bg.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/footer-card-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/footer-card-bg.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/github.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/arrow-black.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/arrow-white.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/catalog-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/catalog-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/chevron-b-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/close-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/close-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/download-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/grid-black.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/grid-white.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/info-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/link-white.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/list-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/list-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/placeholder.svg:
--------------------------------------------------------------------------------
1 |
2 |
36 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/plus-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/right-arrow.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/search-black.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/icons/search-white.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/logo.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/placeholder-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/placeholder-logo.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AI-Native-Dev-Community/ai-native-dev-landscape/a040e16d2f2b095496b6c07bffb36f16d8093121/aind-landing-Page/public/placeholder.png
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/apple-podcast.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/discord.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/github.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/linkedIn.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/spotify.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/threads.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/x.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/public/social/youtube.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/aind-landing-Page/scripts/convert-yaml.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-require-imports */
2 | const fs = require("fs");
3 | const yaml = require("js-yaml");
4 | const path = require("path");
5 |
6 | // Path to your YAML file
7 | const yamlFilePath = path.join(process.cwd(), "public", "tools-data.yaml"); // Update with your actual YAML filename
8 | // Path where you want to save the JSON file (in the public directory)
9 | const jsonFilePath = path.join(process.cwd(), "public", "tools-data.json");
10 |
11 | try {
12 | // Make sure the YAML file exists
13 | if (!fs.existsSync(yamlFilePath)) {
14 | console.error(`YAML file not found at ${yamlFilePath}`);
15 | process.exit(1);
16 | }
17 |
18 | // Read the YAML file
19 | const yamlContent = fs.readFileSync(yamlFilePath, "utf8");
20 |
21 | // Convert YAML to JSON
22 | const jsonData = yaml.load(yamlContent);
23 |
24 | // Write the JSON to the public directory
25 | fs.writeFileSync(jsonFilePath, JSON.stringify(jsonData, null, 2));
26 |
27 | console.log("Successfully converted YAML to JSON");
28 | console.log(`YAML file: ${yamlFilePath}`);
29 | console.log(`JSON file: ${jsonFilePath}`);
30 | } catch (error) {
31 | console.error("Error converting YAML to JSON:", error);
32 | process.exit(1);
33 | }
34 |
--------------------------------------------------------------------------------
/aind-landing-Page/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/aind-landing-Page/type/tools-type.tsx:
--------------------------------------------------------------------------------
1 | export interface Tool {
2 | name: string;
3 | description: string;
4 | date_added: string;
5 | icon_url: string;
6 | oss: boolean;
7 | tags: string[];
8 | verified: boolean;
9 | website_url: string;
10 | popular: boolean;
11 | }
12 |
13 | export interface Category {
14 | description: string;
15 | level: number;
16 | name: string;
17 | tools: Tool[];
18 | }
19 |
20 | export interface Domain {
21 | categories: Category[];
22 | description: string;
23 | level: number;
24 | name: string;
25 | }
26 |
27 | export interface ToolsData {
28 | domains: Domain[];
29 | }
30 |
--------------------------------------------------------------------------------
/aind-landing-Page/util/get-consistent-color.ts:
--------------------------------------------------------------------------------
1 | export const getConsistentColor = (name: string) => {
2 | let hash = 0;
3 | for (let i = 0; i < name.length; i++) {
4 | hash = name.charCodeAt(i) + ((hash << 5) - hash);
5 | }
6 |
7 | let color = "#";
8 | for (let i = 0; i < 3; i++) {
9 | const value = (hash >> (i * 8)) & 0xff;
10 | color += ("00" + value.toString(16)).substr(-2);
11 | }
12 |
13 | return color;
14 | };
15 |
--------------------------------------------------------------------------------