Skip to content

Commit 3a4dba5

Browse files
committed
fix(landing): address QA findings and isolate demo dependencies
1 parent 20acfb9 commit 3a4dba5

79 files changed

Lines changed: 1076 additions & 738 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { cn } from '@sim/emcn'
12
import type { Metadata } from 'next'
23
import { notFound } from 'next/navigation'
34
import type { CompetitorProfile } from '@/lib/compare/data'
@@ -20,6 +21,7 @@ import {
2021
import { BackLink } from '@/app/(landing)/components'
2122
import { JsonLd } from '@/app/(landing)/components/json-ld'
2223
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
24+
import { LANDING_CONTENT_WIDTH, LANDING_GUTTER } from '@/app/(landing)/components/landing-layout'
2325

2426
const baseUrl = SITE_URL
2527

@@ -161,7 +163,7 @@ export default async function ComparisonProviderPage({
161163
<JsonLd data={faqJsonLd} />
162164

163165
<main id='main-content' className='bg-[var(--bg)]'>
164-
<div className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'>
166+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER, 'pt-[112px] max-sm:pt-20')}>
165167
<div className='mb-6'>
166168
<BackLink href='/comparisons' label='Back to comparisons' />
167169
</div>
@@ -204,8 +206,8 @@ export default async function ComparisonProviderPage({
204206

205207
<div className='mt-8 h-px w-full bg-[var(--border)]' />
206208

207-
<div className='mx-auto w-full max-w-[1446px]'>
208-
<div className='mx-12 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'>
209+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER)}>
210+
<div className='border-[var(--border)] border-x'>
209211
{competitor.betterThanAnswer ? (
210212
<>
211213
<section aria-labelledby='better-than-heading' className='px-6 py-10'>

apps/sim/app/(landing)/comparisons/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { cn } from '@sim/emcn'
12
import type { Metadata } from 'next'
23
import Link from 'next/link'
34
import { simProfile } from '@/lib/compare/data'
@@ -8,6 +9,7 @@ import { ALL_COMPETITORS, ensurePeriod, lowercaseFirst } from '@/app/(landing)/c
89
import { ChevronArrow } from '@/app/(landing)/components/chevron-arrow'
910
import { JsonLd } from '@/app/(landing)/components/json-ld'
1011
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
12+
import { LANDING_CONTENT_WIDTH, LANDING_GUTTER } from '@/app/(landing)/components/landing-layout'
1113

1214
const baseUrl = SITE_URL
1315

@@ -101,7 +103,7 @@ export default function ComparisonHubPage() {
101103
<JsonLd data={faqJsonLd} />
102104

103105
<main id='main-content' className='bg-[var(--bg)]'>
104-
<div className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'>
106+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER, 'pt-[112px] max-sm:pt-20')}>
105107
{/* Invisible spacer matching the detail page's BackLink block height/margin, so the divider below lands at the same Y on both pages. */}
106108
<div className='mb-6 h-6' aria-hidden='true' />
107109

@@ -129,7 +131,7 @@ export default function ComparisonHubPage() {
129131

130132
<div className='mt-8 h-px w-full bg-[var(--border)]' />
131133

132-
<div className='mx-auto w-full max-w-[1446px] px-12 max-sm:px-5 max-lg:px-8'>
134+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER)}>
133135
<div className='border-[var(--border)] border-x'>
134136
<section aria-labelledby='all-comparisons-heading' className='pt-10'>
135137
<h2

apps/sim/app/(landing)/components/content-index-page/content-index-loading.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Skeleton } from '@sim/emcn'
1+
import { cn, Skeleton } from '@sim/emcn'
2+
import { LANDING_CONTENT_WIDTH, LANDING_GUTTER } from '@/app/(landing)/components/landing-layout'
23

34
const FEATURED_SKELETON_COUNT = 3
45
const LIST_SKELETON_COUNT = 5
@@ -7,16 +8,16 @@ const LIST_SKELETON_COUNT = 5
78
export function ContentIndexLoading() {
89
return (
910
<section className='bg-[var(--bg)]'>
10-
<div className='mx-auto w-full max-w-[1728px]'>
11-
<div className='px-10 pt-[112px] max-sm:pt-20 max-md:px-7 max-lg:px-8 max-xl:px-9'>
11+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER)}>
12+
<div className='pt-[112px] max-sm:pt-20'>
1213
<Skeleton className='mb-5 h-[20px] w-[60px] rounded-md bg-[var(--surface-hover)]' />
1314
<div className='flex flex-col gap-4 md:flex-row md:items-end md:justify-between'>
1415
<Skeleton className='h-[40px] w-[240px] rounded-[4px] bg-[var(--surface-hover)]' />
15-
<Skeleton className='h-[18px] w-[320px] rounded-[4px] bg-[var(--surface-hover)]' />
16+
<Skeleton className='h-[18px] w-[320px] max-w-full rounded-[4px] bg-[var(--surface-hover)]' />
1617
</div>
1718
</div>
1819

19-
<div className='mx-20 mt-8 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'>
20+
<div className='mt-8 border-[var(--border)] border-x'>
2021
<div className='h-px w-full bg-[var(--border)]' />
2122

2223
<div className='flex max-sm:flex-col'>

apps/sim/app/(landing)/components/content-post-page/content-post-loading.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { Skeleton } from '@sim/emcn'
1+
import { cn, Skeleton } from '@sim/emcn'
2+
import { LANDING_CONTENT_WIDTH, LANDING_GUTTER } from '@/app/(landing)/components/landing-layout'
23

34
/** Shared loading skeleton for a content section's post-detail route. */
45
export function ContentPostLoading() {
56
return (
67
<article className='w-full bg-[var(--bg)]'>
7-
<div className='mx-auto w-full max-w-[1728px] px-10 pt-[112px] max-sm:pt-20 max-md:px-7 max-lg:px-8 max-xl:px-9'>
8+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER, 'pt-[112px] max-sm:pt-20')}>
89
<div className='mb-6'>
910
<Skeleton className='h-[16px] w-[100px] rounded-[4px] bg-[var(--surface-hover)]' />
1011
</div>
@@ -32,8 +33,8 @@ export function ContentPostLoading() {
3233

3334
<div className='mt-8 h-px w-full bg-[var(--border)]' />
3435

35-
<div className='mx-auto w-full max-w-[1728px]'>
36-
<div className='mx-20 border-[var(--border)] border-x max-sm:mx-5 max-lg:mx-8'>
36+
<div className={cn(LANDING_CONTENT_WIDTH, LANDING_GUTTER)}>
37+
<div className='border-[var(--border)] border-x'>
3738
<div className='mx-auto max-w-[900px] px-6 py-16'>
3839
<div className='space-y-4'>
3940
<Skeleton className='h-[16px] w-full rounded-[4px] bg-[var(--surface-hover)]' />

apps/sim/app/(landing)/components/footer/footer.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { MODEL_PROVIDERS_WITH_CATALOGS } from '@/app/(landing)/models/utils'
3535
*/
3636

3737
const LINK_CLASS =
38-
'text-left text-sm text-[var(--text-muted)] transition-colors hover:text-[var(--text-primary)]'
38+
'text-left text-sm text-[var(--text-secondary)] transition-colors hover:text-[var(--text-primary)]'
3939

4040
interface FooterLinkItem {
4141
label: string
@@ -56,8 +56,7 @@ interface FooterProps {
5656

5757
/**
5858
* Platform modules link to their local landing pages (internal link equity
59-
* stays on the ranking pages); docs-only surfaces (MCP, API, Self Hosting)
60-
* and Status remain external.
59+
* stays on the ranking pages); docs-only surfaces remain external.
6160
*/
6261
const PRODUCT_LINKS: FooterItem[] = [
6362
{ label: 'Overview', href: '/platform' },
@@ -70,17 +69,20 @@ const PRODUCT_LINKS: FooterItem[] = [
7069
{ label: 'Logs', href: '/logs' },
7170
{ label: 'MCP', href: 'https://docs.sim.ai/agents/mcp', external: true },
7271
{ label: 'API', href: 'https://docs.sim.ai/api-reference/getting-started', external: true },
72+
{ label: 'CLI', href: 'https://docs.sim.ai/cli', external: true },
7373
{ label: 'Self Hosting', href: 'https://docs.sim.ai/platform/self-hosting', external: true },
74-
{ label: 'Status', href: 'https://status.sim.ai', external: true },
7574
]
7675

7776
const RESOURCES_LINKS: FooterItem[] = [
77+
{ label: 'Customers', href: '/customers' },
7878
{ label: 'Blog', href: '/blog' },
7979
{ label: 'Docs', href: 'https://docs.sim.ai', external: true },
8080
{ label: 'Library', href: '/library' },
8181
{ label: 'Careers', href: '/careers' },
8282
{ label: 'Changelog', href: '/changelog' },
8383
{ label: 'Contact', href: '/contact' },
84+
{ label: 'Status', href: 'https://status.sim.ai', external: true },
85+
{ label: 'Security', href: 'https://trust.sim.ai', external: true },
8486
]
8587

8688
/** Top model providers, sourced from the catalog so labels/hrefs never drift. */
@@ -210,7 +212,9 @@ export function Footer({ showConsentPreferences = false }: FooterProps) {
210212

211213
<FooterWordmarkLoop className='mt-[120px] max-sm:mt-16 max-lg:mt-[88px]' />
212214

213-
<p className='mt-16 text-[var(--text-muted)] text-sm'>© 2026 Sim. All rights reserved.</p>
215+
<p className='mt-16 text-[var(--text-secondary)] text-sm'>
216+
© 2026 Sim. All rights reserved.
217+
</p>
214218
</div>
215219
</footer>
216220
)

apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.test.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,36 @@ vi.mock('@/app/(landing)/components/hero/components/hero-chat-welcome', () => ({
4646
vi.mock('@/app/(landing)/components/hero/components/hero-platform-loop/sidebar-hotspots', () => ({
4747
HERO_TOOLTIP_OFFSET: 8,
4848
}))
49-
vi.mock('@/app/workspace/[workspaceId]/home/components/message-content/components', () => ({
50-
AgentGroup: (props: AgentGroupProps) => {
51-
renderAgentGroup(props)
52-
return <section aria-label={props.agentLabel}>{props.agentLabel}</section>
53-
},
54-
PendingTagIndicator: (props: { label: string }) => {
55-
renderPendingIndicator(props)
56-
return <output>{props.label}</output>
57-
},
58-
ChatContent: ({ content }: { content: string }) => <p>{content}</p>,
59-
QuestionDisplay: () => <p>What would you like to do next?</p>,
49+
vi.mock(
50+
'@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view',
51+
() => ({
52+
AgentGroupView: (props: AgentGroupProps) => {
53+
renderAgentGroup(props)
54+
return <section aria-label={props.agentLabel}>{props.agentLabel}</section>
55+
},
56+
})
57+
)
58+
vi.mock('@/app/(landing)/components/hero/components/hero-chat-loop/hero-tool-call-item', () => ({
59+
HeroToolCallItem: () => null,
60+
}))
61+
vi.mock(
62+
'@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/pending-tag-indicator',
63+
() => ({
64+
PendingTagIndicator: (props: { label: string }) => {
65+
renderPendingIndicator(props)
66+
return <output>{props.label}</output>
67+
},
68+
})
69+
)
70+
vi.mock('@/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-reply', () => ({
71+
HeroChatReply: ({ content }: { content: string }) => <p>{content}</p>,
6072
}))
6173
vi.mock(
6274
'@/app/workspace/[workspaceId]/home/components/message-content/components/question',
63-
() => ({ parseQuestionAnswerMessage: () => undefined })
75+
() => ({
76+
parseQuestionAnswerMessage: () => undefined,
77+
QuestionDisplay: () => <p>What would you like to do next?</p>,
78+
})
6479
)
6580
vi.mock(
6681
'@/app/workspace/[workspaceId]/home/components/user-input/components/send-button/send-button',

apps/sim/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-loop.tsx

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
import { useRef, useState } from 'react'
44
import { Button, cn, Tooltip } from '@sim/emcn'
55
import { Mic, Paperclip, Plus, Slash, X } from '@sim/emcn/icons'
6+
import { HeroChatReply } from '@/app/(landing)/components/hero/components/hero-chat-loop/hero-chat-reply'
7+
import { HeroToolCallItem } from '@/app/(landing)/components/hero/components/hero-chat-loop/hero-tool-call-item'
68
import { HeroChatWelcome } from '@/app/(landing)/components/hero/components/hero-chat-welcome'
79
import { HERO_TOOLTIP_OFFSET } from '@/app/(landing)/components/hero/components/hero-platform-loop/sidebar-hotspots'
810
import { useElapsedReveal } from '@/app/(landing)/hooks/use-elapsed-reveal'
911
import {
10-
AgentGroup,
1112
type AgentGroupItem,
12-
ChatContent,
13-
PendingTagIndicator,
13+
AgentGroupView,
14+
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view'
15+
import {
16+
parseQuestionAnswerMessage,
1417
QuestionDisplay,
15-
} from '@/app/workspace/[workspaceId]/home/components/message-content/components'
16-
import { parseQuestionAnswerMessage } from '@/app/workspace/[workspaceId]/home/components/message-content/components/question'
18+
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/question'
1719
import type { QuestionItem } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags'
20+
import { PendingTagIndicator } from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/pending-tag-indicator'
1821
import { SendButton } from '@/app/workspace/[workspaceId]/home/components/user-input/components/send-button/send-button'
1922
import { ToolCallStatus } from '@/app/workspace/[workspaceId]/home/types'
2023

@@ -207,7 +210,8 @@ export function HeroChatLoop({
207210
<PendingTagIndicator label={phase === 'dispatching' ? 'Dispatching…' : 'Thinking…'} />
208211
)}
209212
{showBuilding && (
210-
<AgentGroup
213+
<AgentGroupView
214+
ToolCallComponent={HeroToolCallItem}
211215
agentName='workflow'
212216
agentLabel='Workflow Agent'
213217
items={WORKFLOW_AGENT_BUILDING_ITEMS}
@@ -220,23 +224,24 @@ export function HeroChatLoop({
220224
)}
221225
{showReply && (
222226
<>
223-
<AgentGroup
227+
<AgentGroupView
228+
ToolCallComponent={HeroToolCallItem}
224229
agentName='workflow'
225230
agentLabel='Workflow Agent'
226231
items={WORKFLOW_AGENT_ITEMS}
227232
defaultExpanded
228233
/>
229-
<AgentGroup
234+
<AgentGroupView
235+
ToolCallComponent={HeroToolCallItem}
230236
agentName='mothership'
231237
agentLabel='Sim'
232238
items={SIM_ITEMS}
233239
defaultExpanded
234240
/>
235-
<ChatContent
241+
<HeroChatReply
236242
content={replyMessage}
237-
messageId='landing-hero-reply'
238243
isStreaming={!replyComplete}
239-
onWorkspaceResourceSelect={onOpenWorkflowResource}
244+
onOpenWorkflowResource={onOpenWorkflowResource}
240245
/>
241246
{replyComplete && (
242247
<QuestionDisplay
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use client'
2+
3+
import { Workflow } from '@sim/emcn/icons'
4+
import { ResourceMention } from '@/app/workspace/[workspaceId]/home/components/message-content/components/resource-mention'
5+
import { useSmoothText } from '@/hooks/use-smooth-text'
6+
7+
interface HeroChatReplyProps {
8+
content: string
9+
isStreaming: boolean
10+
onOpenWorkflowResource: () => void
11+
}
12+
13+
const WORKFLOW_TITLE = 'Lead enrichment'
14+
15+
/** The seeded reply uses the native text pacer and resource chip without workspace data queries. */
16+
export function HeroChatReply({
17+
content,
18+
isStreaming,
19+
onOpenWorkflowResource,
20+
}: HeroChatReplyProps) {
21+
const revealed = useSmoothText(content, isStreaming)
22+
return (
23+
<div className='space-y-4 font-[family-name:var(--font-inter)] text-[var(--text-primary)] text-base leading-[25px] tracking-[0] antialiased'>
24+
{revealed.split('\n\n').map((paragraph, index) => {
25+
const resourceIndex = paragraph.indexOf(WORKFLOW_TITLE)
26+
return (
27+
<p key={index}>
28+
{resourceIndex < 0 ? (
29+
paragraph
30+
) : (
31+
<>
32+
{paragraph.slice(0, resourceIndex)}
33+
<ResourceMention
34+
icon={
35+
<Workflow className='relative top-0.5 size-[12px] shrink-0 text-[var(--text-icon)]' />
36+
}
37+
title={WORKFLOW_TITLE}
38+
onSelect={onOpenWorkflowResource}
39+
/>
40+
{paragraph.slice(resourceIndex + WORKFLOW_TITLE.length)}
41+
</>
42+
)}
43+
</p>
44+
)
45+
})}
46+
</div>
47+
)
48+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Table } from '@sim/emcn/icons'
2+
import { SlackIcon } from '@/components/icons'
3+
import { getToolStatusDisplayTitle } from '@/lib/copilot/tools/tool-display'
4+
import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item'
5+
import { ToolCallRow } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-row'
6+
7+
/** Demo fixtures have known brands, so the landing page never loads the block registry. */
8+
export function HeroToolCallItem({
9+
toolCallId,
10+
toolName,
11+
displayTitle,
12+
status,
13+
}: ToolCallItemProps) {
14+
const Icon =
15+
toolCallId === 'hero-read-slack'
16+
? SlackIcon
17+
: toolCallId === 'hero-read-table'
18+
? Table
19+
: undefined
20+
return (
21+
<ToolCallRow
22+
title={getToolStatusDisplayTitle(displayTitle, status, toolName)}
23+
isExecuting={status === 'executing'}
24+
icon={Icon && <Icon className='size-[14px] shrink-0 text-[var(--text-icon)]' />}
25+
/>
26+
)
27+
}

apps/sim/app/(landing)/components/hero/components/hero-platform-loop/hero-platform-loop-mount.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,7 @@
22

33
import dynamic from 'next/dynamic'
44

5-
/**
6-
* `ssr: false` so the loop's client graph never enters the marketing layout's
7-
* initial bundle. The loop stages Sim's real chat presentation, and that
8-
* surface resolves tool calls against the full block registry - several
9-
* megabytes of client JavaScript that every landing route would otherwise
10-
* carry through the shared components barrel. No page copy lives inside the
11-
* loop, its frame reserves the box, and the painted backdrop is the LCP
12-
* element, so deferring it costs crawlers nothing and causes no layout shift.
13-
*/
5+
/** Loads the interactive demo independently of the server-rendered hero copy and artwork. */
146
const HeroPlatformLoop = dynamic(
157
() =>
168
import('@/app/(landing)/components/hero/components/hero-platform-loop/hero-platform-loop').then(

0 commit comments

Comments
 (0)