Skip to content

Commit 32b6a42

Browse files
authored
improvement(landing): SSR-friendly URL-state filters + cleanup pass + polish (#5298)
* chore(landing): cleanup pass — size-* shorthand, drop redundant refs/memos, changelog useInfiniteQuery - emcn: collapse 29 square h-N w-N pairs to size-* across models/integrations/blog/faq/landing-preview - landing-preview: remove redundant animationKeyRef (functional setState) + dead isDesktopRef - model-directory: hoist static provider options to a module const - auth-modal: drop useMemo over the cheap getBrandConfig() call - changelog: replace useState+fetch pagination with a co-located useInfiniteQuery hook (first page stays SSR-seeded via initialData) * feat(landing): URL-state filters via nuqs (SSR-friendly) + stars fallback bump - integrations grid, models directory: search + category/provider filters now live in the URL (nuqs useQueryStates), server-parsed via createSearchParamsCache so filtered views render server-side (crawlable, shareable) — mirrors the blog index SSR pattern. Search URL writes debounced; in-memory filtering stays instant. - pricing: billing-period toggle moves to ?billing=annual (shareable, no flash). - co-located search-params.ts per page as the single source of truth (server + client). - stars: bump fallback floor 28800 -> 28900 to track the current count. * fix(landing): use the light sim mark (not green) in the integration CTA Replace brandbook/logo/small.png — referenced only by the 'Start automating {service} today' CTA on integration pages — with the black-on-light sim mark so the tile reads as a neutral brand mark beside the partner icon instead of a green block. * fix(landing): calmer release-timeline dot hover Drop the scale-150 + brightness-75 hover (a 50% size jump plus a muddy darken) for an understated opacity-fill (0.85 -> 1) with a slight scale-110, matching the comparison chart's opacity-driven hover language. * fix(landing): keep changelog load-more when initial page is empty; drop inline comments - changelog: getNextPageParam no longer treats an empty server-seeded page 1 as end-of-feed, so a failed/empty initial GitHub fetch still surfaces 'Show more' (addresses Cursor Bugbot). - replace the inline comments added in this branch with TSDoc on the search-param caches (documents the dynamic-render → SSR-filtered behavior), per the codebase's TSDoc-only convention. * revert(landing): keep the original changelog timeline (drop useInfiniteQuery) The React Query conversion of an SSR-seeded, cross-origin, infrequently-changing paginated list introduced caching edge cases (empty-seed load-more, singleton cache serving stale page 1 on client-side nav) without real benefit. Restore the original useState(initialEntries)+fetch timeline, which is simpler, correct, and already review-clean from #5181.
1 parent ad19f7f commit 32b6a42

25 files changed

Lines changed: 198 additions & 74 deletions

File tree

apps/sim/app/(landing)/blog/[slug]/share-button.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ export function ShareButton({ url, title }: ShareButtonProps) {
4141
className='flex items-center gap-1.5 text-[var(--text-muted)] text-sm hover:text-[var(--text-primary)]'
4242
aria-label='Share this post'
4343
>
44-
<Share2 className='h-4 w-4' />
44+
<Share2 className='size-4' />
4545
<span>Share</span>
4646
</button>
4747
</DropdownMenuTrigger>
4848
<DropdownMenuContent align='end'>
4949
<DropdownMenuItem onSelect={handleCopyLink}>
50-
<Duplicate className='h-4 w-4' />
50+
<Duplicate className='size-4' />
5151
{copied ? 'Copied!' : 'Copy link'}
5252
</DropdownMenuItem>
5353
<DropdownMenuItem onSelect={handleShareTwitter}>
54-
<XIcon className='h-4 w-4' />
54+
<XIcon className='size-4' />
5555
Share on X
5656
</DropdownMenuItem>
5757
<DropdownMenuItem onSelect={handleShareLinkedIn}>
58-
<LinkedInIcon className='h-4 w-4' />
58+
<LinkedInIcon className='size-4' />
5959
Share on LinkedIn
6060
</DropdownMenuItem>
6161
</DropdownMenuContent>

apps/sim/app/(landing)/components/auth-modal/auth-modal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useEffect, useMemo, useState } from 'react'
3+
import { useEffect, useState } from 'react'
44
import {
55
Loader,
66
Modal,
@@ -69,7 +69,7 @@ export function AuthModal({ children, defaultView = 'login', source }: AuthModal
6969
const [view, setView] = useState<AuthView>(defaultView)
7070
const [providerStatus, setProviderStatus] = useState<ProviderStatus | null>(null)
7171
const [socialLoading, setSocialLoading] = useState<'github' | 'google' | 'microsoft' | null>(null)
72-
const brand = useMemo(() => getBrandConfig(), [])
72+
const brand = getBrandConfig()
7373

7474
useEffect(() => {
7575
fetchProviderStatus().then(setProviderStatus)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function LandingFAQ({ faqs }: LandingFAQProps) {
6161
</span>
6262
<ChevronDown
6363
className={cn(
64-
'h-3 w-3 shrink-0 text-[var(--text-muted)] transition-transform duration-200',
64+
'size-3 shrink-0 text-[var(--text-muted)] transition-transform duration-200',
6565
isOpen ? 'rotate-180' : 'rotate-0'
6666
)}
6767
aria-hidden='true'

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
196196
onClick={() => setToolsExpanded((p) => !p)}
197197
className='flex cursor-pointer items-center gap-2'
198198
>
199-
<div className='flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center'>
200-
<Blimp className='h-[16px] w-[16px] text-[var(--text-icon)]' />
199+
<div className='flex size-[16px] flex-shrink-0 items-center justify-center'>
200+
<Blimp className='size-[16px] text-[var(--text-icon)]' />
201201
</div>
202202
<span className='text-[var(--text-body)] text-sm'>Sim</span>
203203
<ChevronDown
@@ -218,9 +218,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
218218
<div className='overflow-hidden'>
219219
<div className='flex flex-col gap-1.5 pt-0.5'>
220220
<ToolCallRow
221-
icon={
222-
<Table className='h-[15px] w-[15px] text-[var(--text-muted)]' />
223-
}
221+
icon={<Table className='size-[15px] text-[var(--text-muted)]' />}
224222
title='Read Customer Leads'
225223
/>
226224
</div>
@@ -343,7 +341,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
343341
function ToolCallRow({ icon, title }: { icon: React.ReactNode; title: string }) {
344342
return (
345343
<div className='flex items-center gap-[8px] pl-[24px]'>
346-
<div className='flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center'>{icon}</div>
344+
<div className='flex size-[16px] flex-shrink-0 items-center justify-center'>{icon}</div>
347345
<span className='text-[13px] text-[var(--text-muted)]'>{title}</span>
348346
</div>
349347
)
@@ -390,7 +388,7 @@ function MiniTablePanel() {
390388
return (
391389
<div className='flex h-full w-full flex-col bg-[var(--surface-2)]'>
392390
<div className='flex items-center gap-2 border-[var(--border)] border-b px-3 py-2'>
393-
<Table className='h-[14px] w-[14px] text-[var(--text-icon)]' />
391+
<Table className='size-[14px] text-[var(--text-icon)]' />
394392
<span className='font-medium text-[var(--text-primary)] text-sm'>Customer Leads</span>
395393
</div>
396394
<div className='min-h-0 flex-1 overflow-auto'>
@@ -410,7 +408,7 @@ function MiniTablePanel() {
410408
className='border-[var(--border-1)] border-r border-b bg-[var(--surface-1)] p-0 text-left'
411409
>
412410
<div className='flex items-center gap-1 px-2 py-1.5'>
413-
<Icon className='h-3 w-3 shrink-0 text-[var(--text-icon)]' />
411+
<Icon className='size-3 shrink-0 text-[var(--text-icon)]' />
414412
<span className='font-medium text-[11px] text-[var(--text-primary)]'>
415413
{col.label}
416414
</span>

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-logs/landing-preview-logs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export function LandingPreviewLogs() {
248248
)}
249249
>
250250
{label}
251-
{sortKey === key && <ArrowUpDown className='h-[10px] w-[10px] opacity-60' />}
251+
{sortKey === key && <ArrowUpDown className='size-[10px] opacity-60' />}
252252
</button>
253253
</th>
254254
))}

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-workflow/preview-block-node.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const MODEL_PROVIDER_ICONS: Array<{
8383
{ prefix: 'o4', icon: OpenAIIcon },
8484
{ prefix: 'claude-', icon: AnthropicIcon },
8585
{ prefix: 'gemini-', icon: GeminiIcon },
86-
{ prefix: 'grok-', icon: xAIIcon, size: 'h-[17px] w-[17px]' },
86+
{ prefix: 'grok-', icon: xAIIcon, size: 'size-[17px]' },
8787
{ prefix: 'mistral-', icon: MistralIcon },
8888
]
8989

@@ -219,7 +219,7 @@ export const PreviewBlockNode = memo(function PreviewBlockNode({
219219
<span className='flex min-w-0 flex-1 items-center justify-end gap-2 font-normal text-[14px] text-[var(--text-primary)]'>
220220
{ModelIcon && (
221221
<ModelIcon
222-
className={`inline-block flex-shrink-0 text-[var(--text-primary)] ${modelEntry.size ?? 'h-[14px] w-[14px]'}`}
222+
className={`inline-block flex-shrink-0 text-[var(--text-primary)] ${modelEntry.size ?? 'size-[14px]'}`}
223223
/>
224224
)}
225225
<span className='truncate'>{row.value}</span>

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,13 @@ export function LandingPreview({
121121
}: LandingPreviewProps) {
122122
const [activeView, setActiveView] = useState<SidebarView>(view)
123123
const [activeWorkflowId, setActiveWorkflowId] = useState(workflowId)
124-
const animationKeyRef = useRef(0)
125124
const [animationKey, setAnimationKey] = useState(0)
126125
const [autoTypeHome, setAutoTypeHome] = useState(false)
127126
const [isDesktop, setIsDesktop] = useState(true)
128127

129128
const demoIndexRef = useRef(0)
130129
const demoTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
131130
const autoCycleActiveRef = useRef(true)
132-
const isDesktopRef = useRef(true)
133131

134132
const clearDemoTimer = useCallback(() => {
135133
if (demoTimerRef.current) {
@@ -144,8 +142,7 @@ export function LandingPreview({
144142
if (step.type === 'workflow' && step.workflowId) {
145143
setActiveWorkflowId(step.workflowId)
146144
setActiveView('workflow')
147-
animationKeyRef.current += 1
148-
setAnimationKey(animationKeyRef.current)
145+
setAnimationKey((k) => k + 1)
149146
} else if (step.type === 'home') {
150147
setActiveView('home')
151148
setAutoTypeHome(true)
@@ -168,7 +165,6 @@ export function LandingPreview({
168165

169166
useEffect(() => {
170167
const desktop = window.matchMedia('(min-width: 1024px)').matches
171-
isDesktopRef.current = desktop
172168
setIsDesktop(desktop)
173169
if (!desktop) return
174170
if (!autoplay) return
@@ -188,8 +184,7 @@ export function LandingPreview({
188184
setAutoTypeHome(false)
189185
setActiveWorkflowId(id)
190186
setActiveView('workflow')
191-
animationKeyRef.current += 1
192-
setAnimationKey(animationKeyRef.current)
187+
setAnimationKey((k) => k + 1)
193188
},
194189
[stopAutoCycle]
195190
)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
403403
name={name}
404404
Icon={IconComponent}
405405
className='size-12 rounded-xl border border-[var(--border-1)]'
406-
iconClassName='h-6 w-6'
406+
iconClassName='size-6'
407407
fallbackClassName='text-[20px]'
408408
aria-hidden='true'
409409
/>
@@ -745,7 +745,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
745745
Icon={ToolIcon}
746746
as='span'
747747
className='size-6 rounded-[4px]'
748-
iconClassName='h-3.5 w-3.5'
748+
iconClassName='size-3.5'
749749
fallbackClassName='text-[10px]'
750750
aria-hidden='true'
751751
/>
@@ -935,7 +935,7 @@ export default async function IntegrationPage({ params }: { params: Promise<{ sl
935935
name={name}
936936
Icon={IconComponent}
937937
className='size-14 rounded-xl'
938-
iconClassName='h-7 w-7'
938+
iconClassName='size-7'
939939
fallbackClassName='text-[22px]'
940940
aria-hidden='true'
941941
/>

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Metadata } from 'next'
2+
import type { SearchParams } from 'nuqs/server'
23
import { SITE_URL } from '@/lib/core/utils/urls'
34
import {
45
blockTypeToIconMap,
@@ -11,6 +12,7 @@ import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
1112
import { IntegrationCard } from '@/app/(landing)/integrations/components/integration-card'
1213
import { IntegrationGrid } from '@/app/(landing)/integrations/components/integration-grid'
1314
import { RequestIntegrationModal } from '@/app/(landing)/integrations/components/request-integration-modal'
15+
import { integrationsSearchParamsCache } from '@/app/(landing)/integrations/search-params'
1416

1517
const allIntegrations = INTEGRATIONS
1618
const INTEGRATION_COUNT = allIntegrations.length
@@ -87,7 +89,13 @@ export const metadata: Metadata = {
8789
alternates: { canonical: `${baseUrl}/integrations` },
8890
}
8991

90-
export default function IntegrationsPage() {
92+
export default async function IntegrationsPage({
93+
searchParams,
94+
}: {
95+
searchParams: Promise<SearchParams>
96+
}) {
97+
await integrationsSearchParamsCache.parse(searchParams)
98+
9199
const breadcrumbJsonLd = {
92100
'@context': 'https://schema.org',
93101
'@type': 'BreadcrumbList',

apps/sim/app/(landing)/integrations/components/integration-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function IntegrationRow({ integration, IconComponent }: IntegrationItemPr
6161
name={name}
6262
Icon={IconComponent}
6363
className='size-8 shrink-0 rounded-xl border border-[var(--border-1)]'
64-
iconClassName='h-4 w-4'
64+
iconClassName='size-4'
6565
fallbackClassName='text-[13px]'
6666
aria-hidden='true'
6767
/>

0 commit comments

Comments
 (0)