chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
import type { Metadata } from 'next'
|
||||
import type { SearchParams } from 'nuqs/server'
|
||||
import { SITE_URL } from '@/lib/core/utils/urls'
|
||||
import {
|
||||
blockTypeToIconMap,
|
||||
type FAQItem,
|
||||
INTEGRATIONS,
|
||||
type Integration,
|
||||
POPULAR_WORKFLOWS,
|
||||
} from '@/lib/integrations'
|
||||
import { withFilteredNoindex } from '@/lib/landing/seo'
|
||||
import { JsonLd } from '@/app/(landing)/components/json-ld'
|
||||
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
|
||||
import { IntegrationCard } from '@/app/(landing)/integrations/components/integration-card'
|
||||
import { IntegrationGrid } from '@/app/(landing)/integrations/components/integration-grid'
|
||||
import { RequestIntegrationModal } from '@/app/(landing)/integrations/components/request-integration-modal'
|
||||
import { integrationsSearchParamsCache } from '@/app/(landing)/integrations/search-params'
|
||||
|
||||
const allIntegrations = INTEGRATIONS
|
||||
const INTEGRATION_COUNT = allIntegrations.length
|
||||
const OAUTH_COUNT = allIntegrations.filter((i) => i.authType === 'oauth').length
|
||||
const TRIGGER_INTEGRATION_COUNT = allIntegrations.filter((i) => i.triggerCount > 0).length
|
||||
const TOTAL_TOOL_COUNT = allIntegrations.reduce((sum, i) => sum + i.operationCount, 0)
|
||||
|
||||
/**
|
||||
* Catalog-level FAQ. Questions that read the same for every integration live
|
||||
* here exactly once instead of repeating across all per-integration pages.
|
||||
*/
|
||||
const CATALOG_FAQS: FAQItem[] = [
|
||||
{
|
||||
question: 'How do integrations work in Sim?',
|
||||
answer: `Each integration is a block you drag onto Sim's workflow builder. Together, Sim's ${INTEGRATION_COUNT} integrations expose ${TOTAL_TOOL_COUNT}+ tools that AI agents can call. ${OAUTH_COUNT} connect with one-click OAuth, and the rest use an API key or no authentication at all. Wire blocks together, add an AI agent block for reasoning, and run.`,
|
||||
},
|
||||
{
|
||||
question: 'Are Sim integrations free to use?',
|
||||
answer: `Yes. Sim's free plan includes every integration in the library, all ${INTEGRATION_COUNT} of them, with no credit card required. Create an account at sim.ai and start building.`,
|
||||
},
|
||||
{
|
||||
question: 'Can an AI agent decide when to use an integration?',
|
||||
answer: `Yes. This is the core of Sim. You give an agent access to integration tools and describe the goal in plain language; the agent decides which tools to call, in what order, and how to handle the results. Automations adapt to context instead of breaking when inputs change.`,
|
||||
},
|
||||
{
|
||||
question: 'Can external events trigger my agents automatically?',
|
||||
answer: `Yes. ${TRIGGER_INTEGRATION_COUNT} Sim integrations include real-time webhook triggers. Add a trigger block to your agent, copy its webhook URL into the external service, and every matching event starts your agent instantly, no polling, no delay.`,
|
||||
},
|
||||
{
|
||||
question: 'How many integrations does Sim support?',
|
||||
answer: `Sim supports ${INTEGRATION_COUNT} integrations across messaging, CRMs, databases, developer tools, AI providers, and more, and the catalog grows continually. If a tool you need is missing, request it below and we'll prioritize it.`,
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Unique integration names that appear in popular workflow pairs.
|
||||
* Used for metadata keywords so they stay in sync automatically.
|
||||
*/
|
||||
const TOP_NAMES = [...new Set(POPULAR_WORKFLOWS.flatMap((p) => [p.from, p.to]))].slice(0, 6)
|
||||
|
||||
const baseUrl = SITE_URL
|
||||
|
||||
const INTEGRATIONS_BREADCRUMB_JSON_LD = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{ '@type': 'ListItem', position: 1, name: 'Home', item: baseUrl },
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: 'Integrations',
|
||||
item: `${baseUrl}/integrations`,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/** Curated featured integrations - high-recognition services shown as cards. */
|
||||
const FEATURED_SLUGS = ['slack', 'notion', 'github', 'gmail'] as const
|
||||
|
||||
const bySlug = new Map(allIntegrations.map((i) => [i.slug, i]))
|
||||
const featured = FEATURED_SLUGS.map((s) => bySlug.get(s)).filter(
|
||||
(i): i is Integration => i !== undefined
|
||||
)
|
||||
|
||||
/**
|
||||
* `q`/`category` render a genuinely different server-rendered list (see
|
||||
* search-params.ts), so filtered URLs are noindexed rather than
|
||||
* self-canonicalized — keeps the single indexable URL as the bare catalog
|
||||
* page instead of asking Google to index every filter permutation.
|
||||
*/
|
||||
export async function generateMetadata({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<SearchParams>
|
||||
}): Promise<Metadata> {
|
||||
const { q, category } = await integrationsSearchParamsCache.parse(searchParams)
|
||||
const isFiltered = Boolean(q || category)
|
||||
|
||||
return withFilteredNoindex(
|
||||
{
|
||||
title: 'Integrations',
|
||||
description: `Connect ${INTEGRATION_COUNT}+ apps and services in Sim's AI workspace. Build agents that automate real work with ${TOP_NAMES.join(', ')}, and more.`,
|
||||
keywords: [
|
||||
'AI workspace integrations',
|
||||
'AI agent integrations',
|
||||
'AI agent builder integrations',
|
||||
...TOP_NAMES.flatMap((n) => [`${n} integration`, `${n} automation`]),
|
||||
...allIntegrations.slice(0, 20).map((i) => `${i.name} automation`),
|
||||
],
|
||||
// og:image/twitter:image come from the sibling opengraph-image.tsx -
|
||||
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
|
||||
openGraph: {
|
||||
title: 'Integrations | Sim AI Workspace',
|
||||
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace. Build agents that link ${TOP_NAMES.join(', ')}, and every tool your team uses.`,
|
||||
url: `${baseUrl}/integrations`,
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: 'Integrations | Sim',
|
||||
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace.`,
|
||||
},
|
||||
alternates: { canonical: `${baseUrl}/integrations` },
|
||||
},
|
||||
isFiltered
|
||||
)
|
||||
}
|
||||
|
||||
export default async function IntegrationsPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<SearchParams>
|
||||
}) {
|
||||
await integrationsSearchParamsCache.parse(searchParams)
|
||||
|
||||
const itemListJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ItemList',
|
||||
name: 'Sim AI Workflow Integrations',
|
||||
description: `Complete list of ${INTEGRATION_COUNT}+ integrations available in Sim's AI workspace for building and deploying AI agents.`,
|
||||
url: `${baseUrl}/integrations`,
|
||||
numberOfItems: INTEGRATION_COUNT,
|
||||
itemListElement: allIntegrations.map((integration, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
item: {
|
||||
'@type': 'SoftwareApplication',
|
||||
name: integration.name,
|
||||
description: integration.description,
|
||||
url: `${baseUrl}/integrations/${integration.slug}`,
|
||||
applicationCategory: 'BusinessApplication',
|
||||
featureList: integration.operations.map((o) => o.name),
|
||||
},
|
||||
})),
|
||||
}
|
||||
|
||||
const faqJsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'FAQPage',
|
||||
mainEntity: CATALOG_FAQS.map(({ question, answer }) => ({
|
||||
'@type': 'Question',
|
||||
name: question,
|
||||
acceptedAnswer: { '@type': 'Answer', text: answer },
|
||||
})),
|
||||
}
|
||||
|
||||
return (
|
||||
<section className='bg-[var(--bg)]'>
|
||||
<JsonLd data={INTEGRATIONS_BREADCRUMB_JSON_LD} />
|
||||
<JsonLd data={itemListJsonLd} />
|
||||
<JsonLd data={faqJsonLd} />
|
||||
|
||||
{/* Hero */}
|
||||
<div className='mx-auto w-full max-w-[1460px] px-20 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'>
|
||||
<div className='flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between'>
|
||||
<h1
|
||||
id='integrations-heading'
|
||||
className='text-balance text-[28px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[40px]'
|
||||
>
|
||||
Integrations
|
||||
</h1>
|
||||
<p className='text-[var(--text-muted)] text-sm leading-[150%] tracking-[0.02em] lg:text-base'>
|
||||
Connect every tool your team uses. Build agents that automate real work across{' '}
|
||||
{INTEGRATION_COUNT} apps and services.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Full-width divider */}
|
||||
<div className='mt-8 h-px w-full bg-[var(--border)]' />
|
||||
|
||||
{/* Border-railed content */}
|
||||
<div className='mx-auto w-full max-w-[1460px]'>
|
||||
<div className='mx-20 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'>
|
||||
{/* Featured integrations - top */}
|
||||
{featured.length > 0 && (
|
||||
<>
|
||||
<nav aria-label='Featured integrations' className='flex flex-col sm:flex-row'>
|
||||
{featured.map((integration) => (
|
||||
<IntegrationCard
|
||||
key={integration.type}
|
||||
integration={integration}
|
||||
IconComponent={blockTypeToIconMap[integration.type]}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
<div className='h-px w-full bg-[var(--border)]' />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* All Integrations - search, filters, rows */}
|
||||
<section aria-labelledby='all-integrations-heading'>
|
||||
<div className='px-6 pt-10 pb-4'>
|
||||
<h2
|
||||
id='all-integrations-heading'
|
||||
className='mb-2 text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em] lg:text-[24px]'
|
||||
>
|
||||
All Integrations
|
||||
</h2>
|
||||
</div>
|
||||
<IntegrationGrid integrations={allIntegrations} />
|
||||
</section>
|
||||
|
||||
{/* FAQ */}
|
||||
<section aria-labelledby='integrations-faq-heading' className='px-6 py-10'>
|
||||
<h2
|
||||
id='integrations-faq-heading'
|
||||
className='mb-8 text-[20px] text-[var(--text-primary)] leading-[100%] tracking-[-0.02em]'
|
||||
>
|
||||
Frequently asked questions
|
||||
</h2>
|
||||
<LandingFAQ faqs={CATALOG_FAQS} />
|
||||
</section>
|
||||
|
||||
<div className='h-px w-full bg-[var(--border)]' />
|
||||
|
||||
{/* Integration request */}
|
||||
<div className='flex flex-col items-start gap-3 p-6 sm:flex-row sm:items-center sm:justify-between'>
|
||||
<div>
|
||||
<p className='text-[15px] text-[var(--text-primary)] tracking-[-0.02em]'>
|
||||
Don't see the integration you need?
|
||||
</p>
|
||||
<p className='mt-0.5 text-[var(--text-muted)] text-xs uppercase tracking-[0.1em]'>
|
||||
Let us know and we'll prioritize it.
|
||||
</p>
|
||||
</div>
|
||||
<RequestIntegrationModal />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Closing full-width divider */}
|
||||
<div className='-mt-px h-px w-full bg-[var(--border)]' />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user