mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-21 21:15:56 +08:00
ea70f8dcf119a2b8e48a70d85b406d44d6076dfd
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fc25cfb3c8 |
fix(docs): fix Core Web Vitals regressions on docs.sim.ai (#5630)
* fix(docs): fix Core Web Vitals regressions on docs.sim.ai Empirically measured under real trace-based (devtools) CPU/network throttling against the live site: mobile Performance 59, LCP 9.2s (TTFB 745ms + 8.4s element render delay). - sidebar-components.tsx / [lang]/layout.tsx: the docs sidebar renders every page in the doc tree as a link at once. Next's default viewport-prefetch fired an RSC payload fetch for every one of them on initial load - dozens of concurrent requests competing with the page's own content for bandwidth. Wired fumadocs' documented `sidebar.prefetch` option through to the custom SidebarItem/SidebarFolder components (which were bypassing it entirely, using next/link directly with no prefetch prop) via the `useSidebar()` context hook. - video.tsx: `autoPlay` forces browsers to fetch the full video file immediately on mount regardless of `preload`. Gated actual src loading behind an IntersectionObserver so a page with several of these doesn't pull down every video up front (5MB across 3 requests, in this case). Single shared component - fixes every doc page that embeds one. - proxy.ts: the i18n middleware matcher excluded favicon/robots.txt/etc but not `icon.svg`, so every request for it got routed through i18n negotiation instead of served as a static file, 404ing in production. - next.config.ts: enable productionBrowserSourceMaps - safe since this repo's source is already fully public, real debuggability benefit, zero performance cost. - shiki 4.0.0 -> 4.3.1 (verified: syntax highlighting still renders correctly). Attempted a coordinated fumadocs-core/ui/mdx/openapi upgrade to latest; fumadocs-openapi's v11 factory function became client-only (breaking change beyond its declared peer deps, requiring a component-boundary restructure), so only the safe, verified, docs-exclusive bumps (fumadocs-core/ui/mdx, shiki) are included here - the openapi major bump needs its own dedicated migration PR. Verified via a real production build (dummy env, all 3974 pages including API reference render/build cleanly) and a clean (non-stale) local server: Performance 59 -> 71 measured under real devtools throttling, RSC prefetch requests 63 -> 11, video requests/bytes 3/5MB -> 0. A pre-existing React hydration warning (#418) was found and confirmed present on live production before any of these changes, unrelated to this diff - documented, not blocking. * fix(docs): fall back to eager video loading without IntersectionObserver The lazy-load gate from the previous commit threw before isInView could ever become true in environments lacking IntersectionObserver (older browsers, some embedded webviews), leaving videos permanently source-less instead of falling back to eager loading. * chore(docs): drop non-TSDoc inline comments Repo convention is TSDoc-only, no plain // comments. * fix(docs): accessibility and SEO defects across the docs app Audited with parallel subagents against the accessibility and SEO skill checklists, each fix verified by reading the actual code (not assumed): Accessibility: - lightbox.tsx: focus was never captured/restored on close, and Tab escaped the modal to the page behind it (no focus trap on the single focusable element) - heading.tsx: the per-heading copy-link icon only appeared on hover, invisible to keyboard-only navigation (added peer-focus-visible) - navbar.tsx: active nav tab had no aria-current - response-section.tsx: the status-code dropdown had no aria-haspopup/aria-expanded/role, and no Escape-to-close - workflow-preview.tsx: same focus-trap gap as lightbox.tsx on the expanded-canvas modal SEO: - page.tsx: generateMetadata's hreflang/canonical URLs used a naive String.replace to strip the locale prefix, which also matched "/en" inside unrelated slugs (platform/enterprise, integrations/enrich, platform/self-hosting/environment-variables), corrupting those pages' canonical and alternate-language URLs. Replaced with a prefix-only strip. - structured-data.tsx: the SoftwareApplication JSON-LD block compared url === baseUrl (no trailing slash) against the homepage's actual url (always has a trailing slash), so the condition was always false and this structured data never rendered anywhere, including the homepage. - structured-data.tsx: "Mothership" in the indexed featureList violated the constitution's required language (the agent is "Sim", the surface is "Chat") - this ships in JSON-LD search engines parse. * fix(docs): defer the Ask Sim chat widget's heavy deps until opened The chat panel (useChat from @ai-sdk/react, Streamdown + its CSS) was mounted unconditionally in the root layout on every single page, so its full weight loaded and executed even though the widget starts closed on every page view. Traced via the LCP breakdown insight under real devtools CPU/network throttling: the LCP text element (the intro paragraph) had a ~8s element render delay despite a ~13ms TTFB, and bootup-time attributed ~4.3s of scripting time to a single chunk containing React/ReactDOM's own runtime plus this widget's eagerly-bundled dependencies. Split into a lightweight ask-ai.tsx (just the toggle button + open state) and ask-ai-panel.tsx (the actual chat UI, useChat, Streamdown), loaded via next/dynamic(..., { ssr: false }) only when the user opens the widget. Verified: the panel's chunk now has zero network requests on initial page load. Measured (mobile, devtools throttling, /introduction): - Performance: 69 -> 75 - LCP: 8.0s -> 6.4s - TBT: 260ms -> 130ms The remaining ~6.4s LCP delay traces to the same shared chunk, now identified as core React/ReactDOM hydration cost for this page's sidebar/TOC/breadcrumb tree rather than an isolated bug - a real, larger initiative (hydration architecture, not a surgical fix), documented here rather than rushed. * fix(docs): preserve Ask Sim chat state across close/reopen The panel split unmounted AskAIPanel entirely on close, discarding useChat's message state - reopening always started an empty conversation, unlike the original single-component layout where useChat lived in a component that never unmounted. Fixed by keeping the panel mounted (via a hasOpened flag that never resets) once first opened, and having the panel itself return null when closed rather than being conditionally removed from the tree by its parent - hooks still run every render, so useChat's state persists across visibility toggles. The dynamic import still only fires on the first open, so the initial-load win is unchanged. Verified via a real click-through (open, type, close, reopen): input persists correctly, and the panel chunk still has zero network requests on initial page load. Performance unchanged at 75. * chore(docs): lint fixes (import order, formatting) * fix(docs): fill the Ask Sim UI gap while the panel chunk loads handleOpen set open=true synchronously, hiding the trigger button before the dynamically imported panel had a chance to render anything (next/dynamic renders null by default with no loading option) - on a slow connection neither the button nor the panel was visible. Added a loading fallback in the same fixed position so there's no gap between the button disappearing and the real panel appearing. |
||
|
|
dcbdcb43aa |
chore(deps): upgrade to nextjs 16 (#2203)
* chore(deps): upgrade to nextjs 16 * upgraded fumadocs * ensure vercel uses bun * fix build * fix bui;d * remove redundant vercel.json |