Skip to content

Commit fcf4e02

Browse files
authored
fix(landing): repair Lighthouse-flagged CWV audits on production (#5605)
* fix(landing): repair Lighthouse-flagged CWV audits on production Empirically verified against a live full Lighthouse run of www.sim.ai (production, pre-fix) plus a local build of the exact deployed commit with source maps temporarily enabled for root-causing. Distinguished genuinely failing audits from passing ones already misread as broken. - fetchPriority missing on every LCP hero image: `priority` generates a preload <link> but Next does not auto-add fetchpriority=high to it - confirmed via raw deployed HTML diff. Added explicit fetchPriority='high' to all 5 priority Image usages (hero, enterprise, blog/library post + index cards). - valid-source-maps failing: production ships no source maps at all (productionBrowserSourceMaps defaults false). Enabled it - safe here since this repo's frontend is already fully open source, so no incremental exposure versus Next's default. - image-delivery-insight (55.8KB wasted): feature-integrate-ui.png's `sizes` hint was a flat 1050px regardless of viewport, so mobile fetched the 1920w variant for a ~423px real render. Replaced with a responsive sizes expression derived from the sibling backdrop image's own (already correct) hint, scaled by the callout's documented 125% overhang. - cache-insight (best-fixable portion): _next/static/* filenames are content-hashed and immutable per deploy, but shared one cache rule with unhashed /public assets, capping both at 1-day max-age. Split into two rules - hashed assets now get 1-year immutable, unhashed assets keep the shorter revalidating TTL. Verified via a real build + server that both paths now return the correct distinct header. Investigated and NOT changed (documented, not assumed): - legacy-javascript-insight (14KB): traced via sourcemap to next/dist/build/polyfills/polyfill-module.js - Next's own built-in polyfill bundle, not our code or a dependency, and not exposed via any next.config.ts option. No browserslist misconfiguration on our end (none exists; Next already defaults to its modern target). - forced-reflow-insight: even with source maps present locally, the dominant cost (335-417ms) stayed [unattributed] by Chrome's own profiler, and the small attributed slice was non-deterministic between our own chunk and a third-party script (HubSpot analytics) across runs - not a confident single root cause worth a targeted fix. - render-blocking-insight / network-dependency-tree / bf-cache: bf-cache's actual failure reason is Cache-Control: no-store on the main document - the exact root cause already fixed on staging (PR #5522/#5528, the PublicEnvScript/unstable_noStore fix) but not yet promoted to main/prod. Resolves once that ships, not additional work here. * fix(landing): convert mothership cover from PNG to JPEG (/blog LCP 6.6s -> 2.8s) Ran a full Lighthouse sweep across every public page as requested. /blog scored 73 (LCP 6.6s) while every other page scored 95+ - reproduced consistently across 3 runs, not noise. Traced via lcp-breakdown-insight: the LCP image (mothership/cover.png, 241KB even after the earlier palette compression pass) took 6+ seconds to download on simulated mobile throttling, well beyond what its size should cost. PNG is a poor fit for this illustration's subtle gradients versus JPEG's lossy compression. Verified empirically before converting: same 1920x1080 resolution, visually identical (spot-checked), 241KB -> 65KB (73% smaller). No other cover in the content set uses PNG and benefits the same way (checked copilot/cover.png, the only other PNG cover - already optimal at 64KB, converting it yielded no improvement, left unchanged). Verified fix: /blog score 73->93, LCP 6.6s->2.8s, reproduced across 3 runs. * fix(landing): correct mobile sizes tier, drop non-functional cache rule - integrations-callout: account for FeatureCard's max-lg:grid-cols-1 mobile stack in the sizes hint, verified against Lighthouse's measured mobile render width. - next.config: remove a custom _next/static cache-control rule that never actually fired (confirmed via header-marker test) - Next's own built-in default already applies the correct immutable 1yr cache to that path. * fix(landing): correct sizes underestimate + fix dead .map header rule - integrations-callout: derive sizes from the section's actual grid math (fixed 386px copy column, 40px gap, section gutters) instead of an approximated vw fraction. Verified against a static reproduction of the layout rendered at each Tailwind breakpoint - the old 110vw mobile tier underestimated real render width by ~3% right at the 1023px stack boundary, which could cause the browser to pick a too-small srcset candidate and upscale. - next.config: the .map header rule's trailing `$` was read as a literal character by Next's path-to-regexp source matcher, not a regex anchor, so the rule never matched a real .map URL (confirmed via routes-manifest regex + a live header check). Removed the dead anchor and added a bounded Cache-Control so a future decision to stop shipping source maps isn't undermined by a 1yr immutable cache on already-fetched maps. * fix(llms): serve well-formed llms.txt, remove Mothership + dead static files Both the marketing site and docs site's llms.txt validator errors ("does not appear to contain any links") traced to the same root cause: a static public/llms.txt shadowed a better-written, already-existing dynamic app/llms.txt route, and every "link" in the static files (and in the docs app's auto-generated route) was bare `label: url` text, not Markdown link syntax - so a strict Markdown-link parser found zero matches even though URLs were visibly present. - apps/sim: delete public/llms.txt (dead code, shadowing the properly Markdown-linked app/llms.txt route.ts, confirmed via production headers showing the static file was what actually served). Fix llms-full.txt's Links/Support/Legal sections to use [label](url) syntax, correct a stale "Next.js 15" reference, and replace "Mothership" with "Chat" per the constitution's language rules. - apps/docs: same shadowing issue - delete the orphaned public/llms.txt (also still said "Mothership"). Fix the auto-generated per-page link list in app/llms.txt/route.ts to emit [title](url) instead of "title: url" for every documentation page. * fix(llms): actually include the route.ts fixes from the prior commit The prior commit (3b2d35c) only staged the two deleted public/llms.txt files - these two modified route.ts files (the Mothership/link-format fixes they were meant to accompany) were left unstaged. No new changes, just completing that commit's intent.
1 parent 83c532c commit fcf4e02

13 files changed

Lines changed: 65 additions & 136 deletions

File tree

apps/docs/app/llms.txt/route.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Sim is the open-source AI workspace where teams build, deploy, and manage AI age
4444
4545
## Documentation Overview
4646
47-
This file provides an overview of our documentation. For full content of all pages, see ${baseUrl}/llms-full.txt
47+
This file provides an overview of our documentation. For full content of all pages, see [llms-full.txt](${baseUrl}/llms-full.txt).
4848
4949
## Main Sections
5050
@@ -54,16 +54,16 @@ ${Object.entries(sections)
5454
.split('-')
5555
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
5656
.join(' ')
57-
return `### ${sectionTitle}\n\n${items.map((item) => `- ${item.title}: ${item.url}${item.description ? `\n ${item.description}` : ''}`).join('\n')}`
57+
return `### ${sectionTitle}\n\n${items.map((item) => `- [${item.title}](${item.url})${item.description ? `\n ${item.description}` : ''}`).join('\n')}`
5858
})
5959
.join('\n\n')}
6060
6161
## Additional Resources
6262
63-
- Full documentation content: ${baseUrl}/llms-full.txt
63+
- [Full documentation content](${baseUrl}/llms-full.txt)
6464
- Individual page content: ${baseUrl}/llms.mdx/[page-path]
65-
- API documentation: ${baseUrl}/api-reference/
66-
- Tool integrations: ${baseUrl}/tools/
65+
- [API documentation](${baseUrl}/api-reference/)
66+
- [Tool integrations](${baseUrl}/tools/)
6767
6868
## Statistics
6969

apps/docs/public/llms.txt

Lines changed: 0 additions & 51 deletions
This file was deleted.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export function ContentIndexPage({
9696
sizes='(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw'
9797
className='object-cover'
9898
priority={index < 3}
99+
fetchPriority={index === 0 ? 'high' : undefined}
99100
unoptimized
100101
/>
101102
</div>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export function ContentPostPage({
5252
className='h-auto w-full'
5353
sizes='(max-width: 768px) 100vw, 450px'
5454
priority
55+
fetchPriority='high'
5556
itemProp='image'
5657
unoptimized
5758
/>

apps/sim/app/(landing)/components/features/components/integrations-callout/integrations-callout.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ import { CalloutFrame } from '@/app/(landing)/components/features/components/cal
1717
* the right AND bottom edges bleed past the media stage's clip - a zoomed-in
1818
* peek at part of the product rather than a complete miniature, scaling
1919
* proportionally with the aspect-locked stage. Decorative.
20+
*
21+
* `sizes` is derived directly from the section's grid math rather than
22+
* approximated, then rounded up to the worst-case (peak render/viewport
23+
* ratio) in each tier so the browser never under-fetches:
24+
* `callout = 1.25 * (viewport - 2*gutter - 32px card padding - [40px gap +
25+
* 386px fixed copy column, desktop only])`, gutter = `px-20`/`max-lg:px-8`/
26+
* `max-sm:px-5` from `Features`'s grid, matching `FeatureCard`'s
27+
* `max-lg:grid-cols-1` stack. Peak ratios (verified against a static
28+
* reproduction of this exact layout rendered at each Tailwind breakpoint):
29+
* ~113.3% at the `max-width: 1023px` stacked tier's own upper edge, ~108.6%
30+
* at `1460px` (the container's cap, where render width stops growing with
31+
* viewport - hence the final tier is a flat px value, not a vw fraction).
2032
*/
2133
export function IntegrationsCallout() {
2234
return (
@@ -29,7 +41,7 @@ export function IntegrationsCallout() {
2941
src='/landing/feature-integrate-ui.png'
3042
alt=''
3143
fill
32-
sizes='1050px'
44+
sizes='(max-width: 1023px) 114vw, (max-width: 1460px) 109vw, 1053px'
3345
className='object-cover'
3446
/>
3547
</CalloutFrame>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export function Hero() {
102102
alt=''
103103
fill
104104
priority
105+
fetchPriority='high'
105106
quality={90}
106107
sizes='(max-width: 1460px) 100vw, 1300px'
107108
className='object-cover'

apps/sim/app/(landing)/enterprise/enterprise.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const ENTERPRISE_CONFIG: SolutionsPageConfig = {
6666
<Image
6767
fill
6868
priority
69+
fetchPriority='high'
6970
alt=''
7071
className='object-cover object-center'
7172
sizes='(max-width: 1024px) 100vw, 1300px'

apps/sim/app/llms-full.txt/route.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function GET() {
99
1010
## Overview
1111
12-
Sim is the AI workspace where teams create agents visually with the workflow builder, conversationally through Mothership, or programmatically with the API. Over 100,000 builders use Sim — from startups to Fortune 500 companies. Teams connect their tools and data, build agents that automate real work across systems, and manage them with full observability. SOC2 compliant.
12+
Sim is the AI workspace where teams create agents visually with the workflow builder, conversationally through Chat, or programmatically with the API. Over 100,000 builders use Sim — from startups to Fortune 500 companies. Teams connect their tools and data, build agents that automate real work across systems, and manage them with full observability. SOC2 compliant.
1313
1414
## Product Details
1515
@@ -122,7 +122,7 @@ Built-in table creation and management:
122122
## Technical Architecture
123123
124124
### Frontend
125-
- Next.js 15 with App Router
125+
- Next.js 16 with App Router
126126
- React Flow for the visual builder
127127
- Tailwind CSS for styling
128128
- Zustand for state management
@@ -150,26 +150,26 @@ Built-in table creation and management:
150150
151151
## Links
152152
153-
- **Website**: ${baseUrl}
154-
- **Documentation**: https://docs.sim.ai
155-
- **API Reference**: https://docs.sim.ai/api
156-
- **GitHub**: https://github.com/simstudioai/sim
157-
- **Discord**: https://discord.gg/Hr4UWYEcTT
158-
- **X/Twitter**: https://x.com/simdotai
159-
- **LinkedIn**: https://linkedin.com/company/simstudioai
153+
- [Website](${baseUrl}): Product overview and primary entry point
154+
- [Documentation](https://docs.sim.ai): Product guides and technical reference
155+
- [API Reference](https://docs.sim.ai/api): API documentation
156+
- [GitHub](https://github.com/simstudioai/sim): Open-source codebase
157+
- [Discord](https://discord.gg/Hr4UWYEcTT): Community server
158+
- [X/Twitter](https://x.com/simdotai): Announcements and updates
159+
- [LinkedIn](https://linkedin.com/company/simstudioai): Company page
160160
161161
## Support
162162
163-
- **Email**: help@sim.ai
164-
- **Security Issues**: security@sim.ai
165-
- **Documentation**: https://docs.sim.ai
166-
- **Community Discord**: https://discord.gg/Hr4UWYEcTT
163+
- [Documentation](https://docs.sim.ai): Self-serve guides and reference
164+
- [Community Discord](https://discord.gg/Hr4UWYEcTT): Community support
165+
- Email: help@sim.ai
166+
- Security issues: security@sim.ai
167167
168168
## Legal
169169
170-
- **Terms of Service**: ${baseUrl}/terms
171-
- **Privacy Policy**: ${baseUrl}/privacy
172-
- **Security**: ${baseUrl}/.well-known/security.txt
170+
- [Terms of Service](${baseUrl}/terms): Legal terms
171+
- [Privacy Policy](${baseUrl}/privacy): Data handling practices
172+
- [Security](${baseUrl}/.well-known/security.txt): Vulnerability disclosure policy
173173
`
174174

175175
return new Response(llmsFullContent, {

apps/sim/content/blog/mothership/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ authors:
88
- emir
99
readingTime: 10
1010
tags: [Release, Mothership, Tables, Knowledge Base, Connectors, RAG, Sim]
11-
ogImage: /blog/mothership/cover.png
11+
ogImage: /blog/mothership/cover.jpg
1212
ogAlt: 'Introducing Mothership airship illustration'
1313
about: ['AI Agents', 'Workflow Automation', 'Developer Tools']
1414
timeRequired: PT10M

apps/sim/next.config.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const minimalRegistryAlias: Record<string, string> = useMinimalRegistry
2929
const nextConfig: NextConfig = {
3030
devIndicators: false,
3131
poweredByHeader: false,
32+
// Safe here since this repo's source is already fully public on GitHub -
33+
// no additional exposure versus Next's default (disabled to avoid leaking
34+
// source on the client).
35+
productionBrowserSourceMaps: true,
3236
turbopack: {
3337
root: path.join(import.meta.dirname, '../..'),
3438
resolveAlias: minimalRegistryAlias,
@@ -188,7 +192,11 @@ const nextConfig: NextConfig = {
188192
async headers() {
189193
return [
190194
{
191-
source: '/((?!api/).*\\.(?:svg|jpg|jpeg|png|gif|ico|webp|avif|woff|woff2|ttf|eot))',
195+
// `/public`-served assets keep their path across deploys (no content
196+
// hash), so a shorter TTL + revalidation window bounds how long a
197+
// changed asset can serve stale.
198+
source:
199+
'/((?!api/|_next/static/).*\\.(?:svg|jpg|jpeg|png|gif|ico|webp|avif|woff|woff2|ttf|eot))',
192200
headers: [
193201
{
194202
key: 'Cache-Control',
@@ -245,14 +253,29 @@ const nextConfig: NextConfig = {
245253
},
246254
],
247255
},
248-
// Block access to sourcemap files (defense in depth)
256+
// Block access to sourcemap files (defense in depth). The trailing
257+
// `$` this rule previously ended with is not a regex anchor in Next's
258+
// `source` matcher (path-to-regexp syntax, not raw regex) - it matched
259+
// a literal `$` character, so this rule never actually fired against
260+
// real `.map` URLs. Next already anchors the compiled pattern at both
261+
// ends, so no trailing anchor is needed here.
262+
//
263+
// Also bounds `.map` files to a short, revalidated TTL rather than
264+
// Next's built-in 1yr immutable default for `_next/static/*` - maps
265+
// are content-hashed like their JS, so this isn't about staleness,
266+
// it's so a future decision to stop shipping `productionBrowserSourceMaps`
267+
// isn't undermined by browsers/edges holding old maps for a year.
249268
{
250-
source: '/(.*)\\.map$',
269+
source: '/(.*)\\.map',
251270
headers: [
252271
{
253272
key: 'x-robots-tag',
254273
value: 'noindex',
255274
},
275+
{
276+
key: 'Cache-Control',
277+
value: 'public, max-age=86400, stale-while-revalidate=604800',
278+
},
256279
],
257280
},
258281
// Chat pages - allow iframe embedding from any origin

0 commit comments

Comments
 (0)