Skip to content

Commit a4c10cd

Browse files
committed
improvement(integrations): trim comments and fold catalog updatedAt into integrations.json
1 parent 39703f9 commit a4c10cd

14 files changed

Lines changed: 16573 additions & 16621 deletions

File tree

apps/sim/app/(landing)/components/landing-faq.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,9 @@ interface LandingFAQProps {
1515
}
1616

1717
/**
18-
* Accordion FAQ for landing pages.
19-
*
20-
* Every answer stays mounted and is collapsed via animated height rather than
21-
* being rendered on demand: AI crawlers (GPTBot, ClaudeBot, PerplexityBot) and
22-
* non-rendering search crawlers only read the served HTML, so the full Q&A
23-
* text must exist in the initial document to be indexed or cited. Keeping the
24-
* answers in the DOM also keeps the FAQPage JSON-LD emitted by consuming pages
25-
* aligned with visible content, as Google's structured-data policy requires.
26-
* Questions render as h3 headings so each Q&A forms an extractable section.
18+
* Accordion FAQ for landing pages. Answers stay mounted (collapsed via
19+
* animated height) so non-JS crawlers see the full Q&A text and FAQPage
20+
* JSON-LD always matches visible content.
2721
*/
2822
export function LandingFAQ({ faqs }: LandingFAQProps) {
2923
const baseId = useId()

apps/sim/app/(landing)/integrations/(shell)/[slug]/opengraph-image.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ export const size = {
99
height: 630,
1010
}
1111

12-
/**
13-
* Reads the raw catalog JSON instead of the `@/lib/integrations` barrel: the
14-
* barrel imports `@/blocks/registry`, which is far too heavy for the edge OG
15-
* bundle. The JSON is the same generated source of truth.
16-
*/
17-
const integrations = integrationsJson as readonly Integration[]
12+
/** Raw catalog JSON, not the barrel — keeps `@/blocks/registry` out of the OG bundle. */
13+
const integrations = integrationsJson.integrations as readonly Integration[]
1814
const bySlug = new Map(integrations.map((i) => [i.slug, i]))
1915

2016
const AUTH_LABEL: Record<AuthType, string> = {

apps/sim/app/(landing)/integrations/(shell)/[slug]/page.tsx

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,7 @@ const AUTH_STEP: Record<AuthType, (name: string) => string> = {
8686
none: () => 'No authentication is needed — the block works as soon as you drop it in.',
8787
}
8888

89-
/**
90-
* Human-readable catalog refresh date, shown on the page and emitted as
91-
* `dateModified` so search and answer engines can see content freshness.
92-
*/
89+
/** Human-readable catalog refresh date for the visible last-updated line. */
9390
const UPDATED_AT_DISPLAY = new Date(`${INTEGRATIONS_UPDATED_AT}T00:00:00Z`).toLocaleDateString(
9491
'en-US',
9592
{ year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' }
@@ -134,10 +131,7 @@ function mentionifyPromptForNames(prompt: string, names: readonly string[]): str
134131
return prompt.replace(regex, (match) => `@${match}`)
135132
}
136133

137-
/**
138-
* Lowercases only the first character so acronyms inside tool names survive
139-
* ("Get PR Details" → "get PR Details").
140-
*/
134+
/** Lowercases only the first character so acronyms in tool names survive. */
141135
function lowercaseFirst(value: string): string {
142136
return value.charAt(0).toLowerCase() + value.slice(1)
143137
}
@@ -149,26 +143,15 @@ function toProseList(items: string[]): string {
149143
return `${items.slice(0, -1).join(', ')}, and ${items[items.length - 1]}`
150144
}
151145

152-
/**
153-
* "a" vs "an" for a service name. Vowel-letter heuristic, except U-names,
154-
* which almost always read as "you" ("a Unipile block").
155-
*/
146+
/** "a" vs "an" for a service name; U-names read as "you", so they take "a". */
156147
function articleFor(name: string): string {
157148
return /^[aeio]/i.test(name) ? 'an' : 'a'
158149
}
159150

160151
/**
161-
* Generates the per-integration FAQ from catalog metadata.
162-
*
163-
* Authoring constraints (SEO/GEO):
164-
* - Every answer opens with a direct, self-contained answer and carries
165-
* integration-specific facts (tool counts and names, trigger events, auth
166-
* mode, related integrations), so no two pages share an identical answer.
167-
* - Catalog-generic questions (pricing, agent tool use) live once on the
168-
* /integrations index FAQ instead of repeating across every page —
169-
* duplicated Q&A across many pages is a scaled-content anti-pattern.
170-
* - The full Q&A text is server-rendered by `LandingFAQ` and mirrored 1:1
171-
* into the page's FAQPage JSON-LD.
152+
* Generates the per-integration FAQ. Answers lead with a direct answer and
153+
* carry integration-specific facts; catalog-generic questions live once on
154+
* the /integrations index FAQ instead of repeating across every page.
172155
*/
173156
function buildFAQs(integration: Integration, relatedNames: string[]): FAQItem[] {
174157
const { name, description, operations, triggers, authType } = integration
@@ -294,9 +277,8 @@ export async function generateMetadata({
294277
'AI agent integrations',
295278
'AI agent builder',
296279
],
297-
// og:image / twitter:image come from the sibling `opengraph-image.tsx`
298-
// file convention — Next serves it at a hash-suffixed URL, so hardcoding
299-
// the URL here would 404. File-based metadata is injected automatically.
280+
// og:image/twitter:image come from the sibling opengraph-image.tsx —
281+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
300282
openGraph: {
301283
title: `${name} Integration | Sim AI Workspace`,
302284
description: `Connect ${name} to ${INTEGRATION_COUNT - 1}+ tools using AI agents. ${sentenceWithTerminalPunctuation(truncate(description, 100))}`,
@@ -454,7 +436,6 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
454436
{description}
455437
</p>
456438

457-
{/* Atomic product summary for AI citation (see .claude/rules/landing-seo-geo.md) */}
458439
<p className='sr-only'>
459440
{name} is a {categoryLabel} integration for Sim, the AI workspace where teams build and
460441
deploy AI agents. Sim&apos;s {name} integration provides{' '}

apps/sim/app/(landing)/integrations/(shell)/opengraph-image.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ export const size = {
88
height: 630,
99
}
1010

11-
/**
12-
* Reads the raw catalog JSON instead of the `@/lib/integrations` barrel: the
13-
* barrel imports `@/blocks/registry`, which is far too heavy for the edge OG
14-
* bundle. The JSON is the same generated source of truth.
15-
*/
16-
const integrations = integrationsJson as readonly Integration[]
11+
/** Raw catalog JSON, not the barrel — keeps `@/blocks/registry` out of the OG bundle. */
12+
const integrations = integrationsJson.integrations as readonly Integration[]
1713
const TOTAL_TOOL_COUNT = integrations.reduce((sum, i) => sum + i.operationCount, 0)
1814
const OAUTH_COUNT = integrations.filter((i) => i.authType === 'oauth').length
1915
const TRIGGER_INTEGRATION_COUNT = integrations.filter((i) => i.triggerCount > 0).length

apps/sim/app/(landing)/integrations/(shell)/page.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,8 @@ const TRIGGER_INTEGRATION_COUNT = allIntegrations.filter((i) => i.triggerCount >
2020
const TOTAL_TOOL_COUNT = allIntegrations.reduce((sum, i) => sum + i.operationCount, 0)
2121

2222
/**
23-
* Catalog-level FAQ. Questions that read the same for every integration
24-
* (pricing, agent tool use, how integrations work) live here exactly once —
25-
* repeating identical Q&A across every per-integration page is a
26-
* scaled-content anti-pattern, and Google's FAQ guidance says repeated FAQs
27-
* should be marked up on a single page. Answers lead with a direct answer and
28-
* concrete catalog numbers for AI-engine extraction.
23+
* Catalog-level FAQ. Questions that read the same for every integration live
24+
* here exactly once instead of repeating across all per-integration pages.
2925
*/
3026
const CATALOG_FAQS: FAQItem[] = [
3127
{
@@ -76,9 +72,8 @@ export const metadata: Metadata = {
7672
...TOP_NAMES.flatMap((n) => [`${n} integration`, `${n} automation`]),
7773
...allIntegrations.slice(0, 20).map((i) => `${i.name} automation`),
7874
],
79-
// og:image / twitter:image come from the sibling `opengraph-image.tsx` file
80-
// convention — Next serves it at a hash-suffixed URL, so hardcoding the URL
81-
// here would 404. File-based metadata is injected automatically.
75+
// og:image/twitter:image come from the sibling opengraph-image.tsx —
76+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
8277
openGraph: {
8378
title: 'Integrations | Sim AI Workspace',
8479
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace. Build agents that link ${TOP_NAMES.join(', ')}, and every tool your team uses.`,
@@ -215,7 +210,7 @@ export default function IntegrationsPage() {
215210

216211
<div className='h-px w-full bg-[var(--landing-bg-elevated)]' />
217212

218-
{/* FAQ — catalog-level questions, asked once for all integrations */}
213+
{/* FAQ */}
219214
<section aria-labelledby='integrations-faq-heading' className='px-6 py-10'>
220215
<h2
221216
id='integrations-faq-heading'

apps/sim/app/(landing)/models/(shell)/[provider]/[model]/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ export async function generateMetadata({
5454
`${provider.name} model pricing`,
5555
...model.capabilityTags,
5656
],
57-
// og:image / twitter:image come from the sibling `opengraph-image.tsx`
58-
// file convention — Next serves it at a hash-suffixed URL, so hardcoding
59-
// the URL here would 404. File-based metadata is injected automatically.
57+
// og:image/twitter:image come from the sibling opengraph-image.tsx —
58+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
6059
openGraph: {
6160
title: `${model.displayName} Pricing, Context Window, and Features | Sim`,
6261
description: `${model.displayName} by ${provider.name}: pricing, context window, and model capability details.`,

apps/sim/app/(landing)/models/(shell)/[provider]/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,8 @@ export async function generateMetadata({
5555
`${provider.name} AI models`,
5656
...provider.models.slice(0, 6).map((model) => model.displayName),
5757
],
58-
// og:image / twitter:image come from the sibling `opengraph-image.tsx`
59-
// file convention — Next serves it at a hash-suffixed URL, so hardcoding
60-
// the URL here would 404. File-based metadata is injected automatically.
58+
// og:image/twitter:image come from the sibling opengraph-image.tsx —
59+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
6160
openGraph: {
6261
title: `${provider.name} Models | Sim`,
6362
description: `Explore ${provider.modelCount} ${provider.name} models with pricing and capability details.`,

apps/sim/app/(landing)/models/(shell)/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,8 @@ export const metadata: Metadata = {
6363
'Mistral models',
6464
...TOP_MODEL_PROVIDERS.map((provider) => `${provider} models`),
6565
],
66-
// og:image / twitter:image come from the sibling `opengraph-image.tsx` file
67-
// convention — Next serves it at a hash-suffixed URL, so hardcoding the URL
68-
// here would 404. File-based metadata is injected automatically.
66+
// og:image/twitter:image come from the sibling opengraph-image.tsx —
67+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
6968
openGraph: {
7069
title: 'AI Models Directory | Sim',
7170
description: `Explore ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers with pricing, context windows, and capability details.`,

apps/sim/app/(landing)/og-utils.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,7 @@ interface LandingOgImageProps {
6969
domainLabel?: string
7070
}
7171

72-
/**
73-
* Shared dynamic OG image for landing catalog pages (models, integrations).
74-
* Used from edge OG routes — callers must keep heavy registries (blocks,
75-
* tools, icons) out of their import graph.
76-
*/
72+
/** Shared dynamic OG image for landing catalog pages (models, integrations). */
7773
export async function createLandingOgImage({
7874
eyebrow,
7975
title,

apps/sim/lib/integrations/index.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,19 @@
1414

1515
import { stripVersionSuffix } from '@sim/utils/string'
1616
import integrationsJson from '@/lib/integrations/integrations.json'
17-
import integrationsMeta from '@/lib/integrations/integrations-meta.json'
1817
import type { Integration } from '@/lib/integrations/types'
1918
import { getAllBlockMeta } from '@/blocks/registry'
2019

2120
/** All integrations surfaced in the catalog, ordered by `scripts/generate-docs.ts`. */
22-
export const INTEGRATIONS: readonly Integration[] = integrationsJson as readonly Integration[]
21+
export const INTEGRATIONS: readonly Integration[] =
22+
integrationsJson.integrations as readonly Integration[]
2323

2424
/**
25-
* ISO date (YYYY-MM-DD) of the last real catalog content change, stamped by
26-
* `scripts/generate-docs.ts` only when `integrations.json` actually changes.
25+
* ISO date of the last real catalog change, stamped by `scripts/generate-docs.ts`.
2726
* Drives sitemap `lastModified`, JSON-LD `dateModified`, and the visible
28-
* last-updated line on integration pages — never fabricate freshness here.
27+
* last-updated line on integration pages.
2928
*/
30-
export const INTEGRATIONS_UPDATED_AT: string = integrationsMeta.catalogUpdatedAt
29+
export const INTEGRATIONS_UPDATED_AT: string = integrationsJson.updatedAt
3130

3231
/** A curated `from → to` block-pair workflow surfaced on the landing page. */
3332
export interface PopularWorkflow {

0 commit comments

Comments
 (0)