diff --git a/apps/sim/app/(landing)/comparisons/[provider]/page.tsx b/apps/sim/app/(landing)/comparisons/[provider]/page.tsx index 000827730f5..f0c18db4341 100644 --- a/apps/sim/app/(landing)/comparisons/[provider]/page.tsx +++ b/apps/sim/app/(landing)/comparisons/[provider]/page.tsx @@ -18,11 +18,12 @@ import { QuestionSections } from '@/app/(landing)/comparisons/components/questio import { getLibraryPostsForCompetitor } from '@/app/(landing)/comparisons/library-links' import { ALL_COMPETITORS, - betterThanQuestion, + answersQuestionsInPlace, buildBottomLine, buildComparisonFaqs, getCompetitorBySlug, getLatestVerifiedDate, + getQuestionSectionGroups, SIM_LATEST_VERIFIED, } from '@/app/(landing)/comparisons/utils' import { BackLink, ContentRelatedPosts } from '@/app/(landing)/components' @@ -96,6 +97,13 @@ export default async function ComparisonProviderPage({ } const faqs = buildComparisonFaqs(competitor) + const questionGroups = getQuestionSectionGroups(competitor) + const { lead } = questionGroups + const leadAnswer = competitor.leadAnswer ? ( +

+ +

+ ) : null const relatedPosts = await getLibraryPostsForCompetitor(competitor) const verdict = buildBottomLine(competitor) const CompetitorIcon = competitor.brand?.icon @@ -200,7 +208,7 @@ export default async function ComparisonProviderPage({ > Sim vs {competitor.name} - {competitor.leadQuestion && competitor.leadAnswer ? ( + {lead ? (
- {competitor.leadQuestion} + {lead.question} -

- -

-

- Every fact below is sourced and dated, last verified {verifiedDate}. -

+ {leadAnswer}
) : ( - <> - {competitor.leadAnswer ? ( -

- -

- ) : null} -

- Sim is the open-source AI workspace where teams build, deploy, and manage AI - agents visually, conversationally, or with code. Here is how Sim compares to{' '} - {competitor.name} on platform architecture, AI capabilities, integrations, - pricing, security, and support. Every fact below is sourced and dated, last - verified {verifiedDate}. -

- + leadAnswer )} +

+ {lead + ? null + : `Sim is the open-source AI workspace where teams build, deploy, and manage AI agents visually, conversationally, or with code. Here is how Sim compares to ${competitor.name} on platform architecture, AI capabilities, integrations, pricing, security, and support. `} + Every fact below is sourced and dated, last verified {verifiedDate}. +

Sim is an open-source AI workspace for building, deploying, and managing AI agents. This page compares Sim to {competitor.name} across platform architecture, AI @@ -254,25 +250,7 @@ export default async function ComparisonProviderPage({

) : null} - {competitor.betterThanAnswer ? ( - <> -
-

- {betterThanQuestion(competitor)} -

-

- -

-
-
- - ) : null} - {competitor.questions?.intro ? ( - - ) : null} +
) : null} - {competitor.questions?.glance ? ( - - ) : null} +

- {competitor.questions?.closing ? ( - - ) : null} +

- {competitor.questions ? null : ( + {answersQuestionsInPlace(competitor) ? null : (

({ - question, - answer: proseToText(answer), - })) + if (answersQuestionsInPlace(competitor)) { + const { lead, opening, glance, closing } = getQuestionSectionGroups(competitor) + return [...(lead ? [lead] : []), ...opening, ...glance, ...closing].map( + ({ question, answer }) => ({ question, answer: proseToText(answer) }) + ) } const name = competitor.name const facts = competitor.facts @@ -178,27 +178,32 @@ export function buildComparisonFaqs(competitor: CompetitorProfile): ComparisonFa return faqs } -/** Heading of the section answering {@link CompetitorProfile.betterThanAnswer}. */ -export function betterThanQuestion(competitor: CompetitorProfile): string { - return `Is Sim better than ${competitor.name}?` +/** + * Whether the page answers its buyer questions as sections (see + * {@link CompetitorProfile.questions}). Such a page omits the FAQ block, and its FAQ structured + * data lists those questions instead. + */ +export function answersQuestionsInPlace(competitor: CompetitorProfile): boolean { + return competitor.questions !== undefined } /** - * The buyer questions the page answers in their own sections, grouped by position: `opening` - * (the lead, "better than", and intro questions) ahead of the product definitions, `glance` + * The buyer questions the page answers in their own sections, by position: `lead` in the hero, + * `opening` (the "better than" and intro questions) ahead of the product definitions, `glance` * after the at-a-glance table, and `closing` after the documented limitations. */ export function getQuestionSectionGroups(competitor: CompetitorProfile): { + lead: ComparisonQuestion | undefined opening: ComparisonQuestion[] glance: ComparisonQuestion[] closing: ComparisonQuestion[] } { const { leadQuestion, leadAnswer, betterThanAnswer, questions } = competitor return { + lead: leadQuestion && leadAnswer ? { question: leadQuestion, answer: leadAnswer } : undefined, opening: [ - ...(leadQuestion && leadAnswer ? [{ question: leadQuestion, answer: leadAnswer }] : []), ...(betterThanAnswer - ? [{ question: betterThanQuestion(competitor), answer: betterThanAnswer }] + ? [{ question: `Is Sim better than ${competitor.name}?`, answer: betterThanAnswer }] : []), ...(questions?.intro ?? []), ], @@ -207,12 +212,6 @@ export function getQuestionSectionGroups(competitor: CompetitorProfile): { } } -/** Every question from {@link getQuestionSectionGroups}, in page order. */ -function getQuestionSections(competitor: CompetitorProfile): ComparisonQuestion[] { - const { opening, glance, closing } = getQuestionSectionGroups(competitor) - return [...opening, ...glance, ...closing] -} - function proseToText(prose: Prose): string { return prose.map((segment) => (typeof segment === 'string' ? segment : segment.text)).join('') } diff --git a/apps/sim/app/llms-full.txt/route.ts b/apps/sim/app/llms-full.txt/route.ts index f955ffdf4c7..f1ed68e438a 100644 --- a/apps/sim/app/llms-full.txt/route.ts +++ b/apps/sim/app/llms-full.txt/route.ts @@ -80,11 +80,11 @@ function isoDate(date: Date): string { return date.toISOString().slice(0, 10) } -/** The key facts of one `/comparisons/{id}` page as markdown, from the same profile data. */ function questionMarkdown({ question, answer }: ComparisonQuestion): string { return `**${question}**\n\n${proseToMarkdown(answer)}` } +/** The key facts of one `/comparisons/{id}` page as markdown, from the same profile data. */ function comparisonMarkdown(competitor: CompetitorProfile): string { const verdict = buildBottomLine(competitor) const questions = getQuestionSectionGroups(competitor) @@ -104,7 +104,11 @@ function comparisonMarkdown(competitor: CompetitorProfile): string { `### Sim vs ${competitor.name}`, `URL: ${toSiteUrl(`/comparisons/${competitor.id}`)} ยท Facts last verified ${isoDate(verified)}`, `${competitor.name}: ${competitor.oneLiner}`, - competitor.leadAnswer && !competitor.leadQuestion ? proseToMarkdown(competitor.leadAnswer) : '', + questions.lead + ? questionMarkdown(questions.lead) + : competitor.leadAnswer + ? proseToMarkdown(competitor.leadAnswer) + : '', ...questions.opening.map(questionMarkdown), competitor.glance ? [ diff --git a/apps/sim/lib/compare/data/types.ts b/apps/sim/lib/compare/data/types.ts index fa9af61bb9e..68e82d57727 100644 --- a/apps/sim/lib/compare/data/types.ts +++ b/apps/sim/lib/compare/data/types.ts @@ -258,8 +258,8 @@ export interface CompetitorProfile { */ leadAnswer?: Prose /** - * The question {@link leadAnswer} answers. When set, the lead renders under it as the page's - * first section, in place of the generic introduction. + * The question {@link leadAnswer} answers. When set, the hero shows the lead answer under it as + * a question heading, in place of the generic introduction. */ leadQuestion?: string /**