Files
sim/apps
Waleed 8ae353828c fix(landing): fix oversized HTML and severe LCP on integration/comparison pages (#5634)
* fix(landing): fix oversized HTML and severe LCP on integration/comparison pages

Google Search Console flagged /integrations, /integrations/slack, and
several others for exceeding Googlebot's 2MB uncompressed-HTML crawl
limit, plus severe LCP on /integrations, /integrations/slack,
/integrations/hubspot, /comparison/flowise, and /integrations/hugging-face.

Root cause 1 - /integrations/slack was 5.2MB (measured on production).
The "Agent templates" section renders every template matching
getTemplatesForBlock(type) with no cap - Slack is referenced as
alsoIntegrations in 445 templates (vs 52-126 for Salesforce/Gmail/
HubSpot), so its detail page embedded hundreds of full template cards
in the initial HTML/RSC payload. Capped to 12, consistent with the
same file's existing related-integrations cap of 4.

Root cause 2 - /integrations was 1.83MB, right at the limit. The client
IntegrationGrid component receives the full Integration[] as props
(needed for instant client-side search), which serializes every
integration's complete operations/triggers arrays - including full
per-operation description sentences - into the initial payload purely
to build a search index. Added IntegrationSummary + toIntegrationSummary
(lib/integrations): the same searchable surface (name, description,
operation names, trigger names) precomputed server-side into one
lowercased string, dropping the full operations/triggers data the
client never actually renders. Measured: 627KB -> 142KB of embedded
integration data (77% reduction).

Verified via a real production build + curl:
- /integrations: 1.83MB -> 1.31MB
- /integrations/slack: 5.2MB -> 355KB (93% reduction)
- /integrations/hubspot: 901KB -> 452KB

Verified via Lighthouse (mobile, devtools throttling, matching what
real users on a typical device experience) against both live production
and the fixed local build:
- /integrations: 47 -> 96 (LCP unmeasured->2.1s, TBT 2,770ms->110ms)
- /integrations/slack: 47 -> 96 (LCP 9.7s -> 1.9s)
- /integrations/hubspot: 72 -> 97 (LCP 9.1s -> 1.9s)
- /integrations/hugging-face: 72 -> 95 (LCP 9.2s -> 2.3s, reproduced
  twice on production before fixing - not a fluke)
- /comparison/flowise: 71 -> 95 (LCP 9.8s -> 2.1s)

The last two aren't Slack-style template-count outliers (4 and 0
alsoIntegrations references respectively) - shrinking the shared
IntegrationGrid client bundle appears to have reduced a shared chunk
loaded broadly across landing pages, benefiting pages beyond the ones
directly touched.

Also checked and confirmed already healthy, no action needed:
/integrations/salesforce, /integrations/gmail, /integrations/amazon-dynamodb,
/comparison/tines, /models/xai/grok-4-20-multi-agent-0309.

* fix(landing): restore full-fidelity search and template priority

Two real regressions from the previous commit, both confirmed by
Greptile and Cursor Bugbot independently:

- lib/integrations: IntegrationSummary.searchText joined every field
  into one string, so (a) it dropped operation/trigger descriptions
  entirely (a search for API-specific terms that only appear in an
  operation's description, not its name, silently stopped matching),
  and (b) a single joined string lets a query span a field boundary
  (e.g. matching across the tail of a name and the head of the next
  field) that the original per-field search never allowed. Reverted to
  a searchFields array - same content as the original per-field index
  (name, description, every operation's name+description, every
  trigger's name), just precomputed server-side instead of shipping
  the full Integration objects. Grid filtering goes back to
  `.some(field => field.includes(q))`, matching the exact original
  matching semantics.
- blocks/registry.ts + integrations/[slug]/page.tsx: capping
  getTemplatesForBlock's result with a plain .slice(0, 12) kept
  whatever 12 templates happened to iterate first in registry
  insertion order - for a high-connectivity integration like Slack,
  that can be entirely alsoIntegrations matches from unrelated blocks,
  silently dropping the integration's own owned templates and any
  marked featured. Added an explicit isOwner flag to
  ScopedBlockTemplate (the registry function already computes this
  internally, just wasn't surfacing it) and sort owner-first,
  featured-second before slicing.

* fix(landing): sort featured templates ahead of owned, not just as a tiebreaker

The previous sort (owner-first, featured as a tiebreaker within each
owner tier) meant an integration with 12+ owned templates filled the
entire cap with non-featured owned templates before any featured
related template (reached via alsoIntegrations) was ever considered -
Slack hits this case. Swapped the sort so featured (owned or related)
ranks first, then owned-but-not-featured, so a curated featured
template can no longer be sliced away by a pile of ordinary owned ones.
2026-07-13 11:23:56 -07:00
..