From 017d8e60b2558b84dba43d00fcfdc6f6c97085d8 Mon Sep 17 00:00:00 2001 From: waleed Date: Wed, 1 Jul 2026 11:17:26 -0700 Subject: [PATCH 1/5] improvement(landing): react-doctor health pass across the landing surface - shared JsonLd server component with HTML-safe serialization (replaces 32 inline dangerouslySetInnerHTML JSON-LD sites; structured-data output semantically identical, XSS hardened) - state/effect fixes: derive-instead-of-sync, phase state machines, useSyncExternalStore for media query, handler-only state to refs - structural splits for one-component-per-file and non-component export isolation - mechanical: combined iterations, hoisted Intl formatters/static values, immutable sorts, stable keys, a11y labels, focus rings, SVG precision - ES2023 lib for apps/sim to allow toSorted/toReversed --- apps/sim/app/(landing)/blog/[slug]/page.tsx | 6 +- .../(landing)/blog/[slug]/share-button.tsx | 1 + .../app/(landing)/blog/authors/[id]/page.tsx | 6 +- apps/sim/app/(landing)/blog/page.tsx | 6 +- apps/sim/app/(landing)/blog/tags/page.tsx | 6 +- apps/sim/app/(landing)/careers/careers.tsx | 6 +- .../careers/components/job-board/index.ts | 3 +- .../components/job-board/job-board.tsx | 6 +- .../components/job-board/job-groups.tsx | 44 +--- .../careers/components/job-board/utils.ts | 44 ++++ .../changelog-timeline/changelog-timeline.tsx | 8 +- apps/sim/app/(landing)/changelog/utils.ts | 27 +- .../components/auth-modal/auth-modal.tsx | 67 ++--- .../components/features/features.tsx | 22 +- .../(landing)/components/footer/footer.tsx | 5 +- .../components/hero-visual/block-handles.tsx | 40 +++ .../components/hero-visual/hero-visual.tsx | 6 +- .../components/hero-visual/stage-home.tsx | 6 +- .../hero-visual/workflow-block-content.tsx | 58 +++++ .../components/hero-visual/workflow-block.tsx | 87 +------ .../home-structured-data.tsx | 242 +++++++++--------- apps/sim/app/(landing)/components/index.ts | 2 + .../app/(landing)/components/json-ld/index.ts | 2 + .../(landing)/components/json-ld/json-ld.tsx | 50 ++++ .../landing-preview-chat/chat-input.tsx | 1 + .../landing-preview-chat.tsx | 34 ++- .../landing-preview-files.tsx | 6 +- .../landing-preview-home.tsx | 2 +- .../landing-preview-knowledge.tsx | 4 +- .../landing-preview-logs.tsx | 3 +- .../landing-preview-resource.tsx | 14 +- .../landing-preview-resource/utils.tsx | 13 + .../landing-preview-tables.tsx | 9 +- .../preview-block-node.tsx | 8 +- .../landing-preview-workflow/workflow-data.ts | 5 +- .../hooks/use-landing-submit.ts | 2 +- .../landing-preview/landing-preview-mount.tsx | 2 +- .../landing-preview/landing-preview.tsx | 41 ++- .../lifecycle-icons/lifecycle-icons.tsx | 17 +- .../components/iso-marks/iso-cube-grid.tsx | 8 +- .../components/iso-marks/iso-cube-row.tsx | 8 +- .../components/navbar-shell/navbar-shell.tsx | 8 +- .../components/sim-wordmark/sim-wordmark.tsx | 8 +- .../(landing)/components/og-sim-logo/index.ts | 1 + .../components/og-sim-logo/og-sim-logo.tsx | 21 ++ .../platform-card-row/platform-card-row.tsx | 2 +- .../platform-structured-data.tsx | 8 +- .../components/legal-block/legal-block.tsx | 13 +- .../legal-block-group/legal-block-group.tsx | 2 +- .../site-structured-data.tsx | 100 ++++---- .../solutions-card-row/solutions-card-row.tsx | 2 +- .../solutions-structured-data.tsx | 8 +- .../thinking-loader/thinking-loader.tsx | 5 +- .../components/contact-form/contact-form.tsx | 16 +- .../components/integration-cta-button.tsx | 2 +- .../components/template-card-button.tsx | 2 +- .../integrations/(shell)/[slug]/page.tsx | 108 ++++---- .../(landing)/integrations/(shell)/page.tsx | 44 ++-- apps/sim/app/(landing)/landing-analytics.tsx | 9 - .../(shell)/[provider]/[model]/page.tsx | 16 +- .../models/(shell)/[provider]/page.tsx | 16 +- .../sim/app/(landing)/models/(shell)/page.tsx | 43 ++-- .../(landing)/models/components/constants.ts | 7 +- .../components/model-comparison-charts.tsx | 21 +- .../models/components/model-primitives.tsx | 79 ------ .../model-primitives/featured-model-card.tsx | 31 +++ .../featured-provider-card.tsx | 22 ++ .../components/model-primitives/index.ts | 3 + .../model-primitives/provider-icon.tsx | 31 +++ .../components/model-timeline-chart.tsx | 14 +- apps/sim/app/(landing)/models/utils.ts | 53 ++-- apps/sim/app/(landing)/og-utils.tsx | 22 +- apps/sim/app/(landing)/partners/page.tsx | 13 +- .../pricing-plans/pricing-plans.tsx | 31 +-- .../pricing-structured-data.tsx | 155 ++++++----- apps/sim/app/(landing)/pricing/pricing.tsx | 27 +- apps/sim/app/(landing)/track-landing-cta.ts | 10 + apps/sim/tsconfig.json | 1 + 78 files changed, 992 insertions(+), 889 deletions(-) create mode 100644 apps/sim/app/(landing)/careers/components/job-board/utils.ts create mode 100644 apps/sim/app/(landing)/components/hero/components/hero-visual/block-handles.tsx create mode 100644 apps/sim/app/(landing)/components/hero/components/hero-visual/workflow-block-content.tsx create mode 100644 apps/sim/app/(landing)/components/json-ld/index.ts create mode 100644 apps/sim/app/(landing)/components/json-ld/json-ld.tsx create mode 100644 apps/sim/app/(landing)/components/landing-preview/components/landing-preview-resource/utils.tsx create mode 100644 apps/sim/app/(landing)/components/og-sim-logo/index.ts create mode 100644 apps/sim/app/(landing)/components/og-sim-logo/og-sim-logo.tsx delete mode 100644 apps/sim/app/(landing)/models/components/model-primitives.tsx create mode 100644 apps/sim/app/(landing)/models/components/model-primitives/featured-model-card.tsx create mode 100644 apps/sim/app/(landing)/models/components/model-primitives/featured-provider-card.tsx create mode 100644 apps/sim/app/(landing)/models/components/model-primitives/index.ts create mode 100644 apps/sim/app/(landing)/models/components/model-primitives/provider-icon.tsx create mode 100644 apps/sim/app/(landing)/track-landing-cta.ts diff --git a/apps/sim/app/(landing)/blog/[slug]/page.tsx b/apps/sim/app/(landing)/blog/[slug]/page.tsx index d27f97e760d..42371752d9a 100644 --- a/apps/sim/app/(landing)/blog/[slug]/page.tsx +++ b/apps/sim/app/(landing)/blog/[slug]/page.tsx @@ -8,6 +8,7 @@ import { buildPostGraphJsonLd, buildPostMetadata } from '@/lib/blog/seo' import { getBaseUrl } from '@/lib/core/utils/urls' import { ShareButton } from '@/app/(landing)/blog/[slug]/share-button' import { BackLink } from '@/app/(landing)/components' +import { JsonLd } from '@/app/(landing)/components/json-ld' export const dynamicParams = false @@ -37,10 +38,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string return (
- `); `>` and `&` are + * escaped for completeness, and the JS line/paragraph separators (U+2028/U+2029) + * keep the payload valid inside inline scripts. + */ +const HTML_ESCAPES: Record = { + '<': '\\u003c', + '>': '\\u003e', + '&': '\\u0026', + [String.fromCharCode(0x2028)]: '\\u2028', + [String.fromCharCode(0x2029)]: '\\u2029', +} + +const UNSAFE_HTML_CHARS = new RegExp(`[<>&${String.fromCharCode(0x2028, 0x2029)}]`, 'g') + +/** + * Serialize structured data for an inline `application/ld+json` script. Plain + * `JSON.stringify` does not HTML-escape, so a `` (or stray `<`) in the + * data would break out of the script tag and become an XSS sink. Escaping these + * characters as unicode escapes keeps the JSON valid and semantically identical, + * so crawlers read the exact same graph — SEO output is unchanged. + */ +function serializeJsonLd(data: JsonLdData): string { + return JSON.stringify(data).replace(UNSAFE_HTML_CHARS, (char) => HTML_ESCAPES[char]) +} + +export type JsonLdData = Record + +interface JsonLdProps { + data: JsonLdData +} + +/** + * Server-rendered JSON-LD `