From 155d4edc7f036411573138b888ec7e55f2508393 Mon Sep 17 00:00:00 2001 From: bangwu Date: Wed, 20 May 2026 10:33:20 +0800 Subject: [PATCH] perf: fix manifest, add preconnect hints, dynamic devtools, and loading indicator - Fill missing manifest.json name/short_name/description for PWA install - Add preconnect/dns-prefetch links for unpkg.com CDN - Make TanStack DevTools dynamically imported (tree-shaken in production) - Add top loading bar animation during client-side route transitions - Remove unused jsdom and @testing-library/dom devDependencies Co-Authored-By: Claude Opus 4.7 --- apps/site/package.json | 2 - apps/site/public/manifest.json | 13 ++- apps/site/src/routes/__root.tsx | 51 ++++++---- apps/site/src/routes/_site/route.lazy.tsx | 10 ++ apps/site/src/styles.css | 6 ++ pnpm-lock.yaml | 111 +++++++++++----------- 6 files changed, 118 insertions(+), 75 deletions(-) diff --git a/apps/site/package.json b/apps/site/package.json index df6669b..b0e401e 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -33,13 +33,11 @@ "@tanstack/devtools-vite": "^0.3.12", "@tanstack/react-devtools": "^0.7.11", "@tanstack/react-router-devtools": "^1.166.2", - "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@types/node": "^22.19.13", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.4", - "jsdom": "^27.4.0", "typescript": "^5.9.3", "vite": "^7.3.1", "vitest": "^3.2.4" diff --git a/apps/site/public/manifest.json b/apps/site/public/manifest.json index 1dd9112..5672389 100644 --- a/apps/site/public/manifest.json +++ b/apps/site/public/manifest.json @@ -1 +1,12 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} +{ + "name": "Memos Embed", + "short_name": "MemosEmbed", + "description": "Embeddable memo cards for Memos — iframe, Web Component, and React snippets", + "icons": [ + { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, + { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } + ], + "theme_color": "#111827", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/apps/site/src/routes/__root.tsx b/apps/site/src/routes/__root.tsx index 2d7c2ef..81d6ad8 100644 --- a/apps/site/src/routes/__root.tsx +++ b/apps/site/src/routes/__root.tsx @@ -1,11 +1,10 @@ -import { TanStackDevtools } from "@tanstack/react-devtools"; import { createRootRouteWithContext, HeadContent, Scripts, useLocation, } from "@tanstack/react-router"; -import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; +import { type ReactNode, useEffect, useState } from "react"; import StaticHeadContent from "@/components/StaticHeadContent"; import { isStaticMarketingPath } from "@/lib/route-mode"; import { buildPageHead, SITE_DESCRIPTION } from "@/lib/site-meta"; @@ -15,8 +14,6 @@ import appCss from "../styles.css?url"; export const Route = createRootRouteWithContext()({ beforeLoad: async () => { - // Other redirect strategies are possible; see - // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect if (typeof document !== "undefined") { document.documentElement.setAttribute("lang", getLocale()); } @@ -44,6 +41,8 @@ export const Route = createRootRouteWithContext()({ rel: "stylesheet", href: appCss, }, + { rel: "preconnect", href: "https://unpkg.com" }, + { rel: "dns-prefetch", href: "https://unpkg.com" }, ], }), @@ -63,21 +62,39 @@ function RootDocument({ children }: { children: React.ReactNode }) { {children} - {showDevtools ? ( - , - }, - ]} - /> - ) : null} + {showDevtools ? : null} {isMarketingPage ? null : } ); } + +function DevToolsHost() { + const [plugins, setPlugins] = useState(null); + + useEffect(() => { + Promise.all([ + import("@tanstack/react-devtools"), + import("@tanstack/react-router-devtools"), + ]).then(([devtoolsMod, routerMod]) => { + const TanStackDevtools = devtoolsMod.TanStackDevtools; + const TanStackRouterDevtoolsPanel = routerMod.TanStackRouterDevtoolsPanel; + + setPlugins([ + , + }, + ]} + />, + ]); + }); + }, []); + + if (!plugins) return null; + return <>{plugins}; +} diff --git a/apps/site/src/routes/_site/route.lazy.tsx b/apps/site/src/routes/_site/route.lazy.tsx index 00b6add..1449bbf 100644 --- a/apps/site/src/routes/_site/route.lazy.tsx +++ b/apps/site/src/routes/_site/route.lazy.tsx @@ -2,6 +2,7 @@ import { createLazyFileRoute, Outlet, useLocation, + useRouterState, } from "@tanstack/react-router"; import { useId } from "react"; import Footer from "@/components/Footer"; @@ -16,11 +17,20 @@ export const Route = createLazyFileRoute("/_site")({ function SiteLayout() { const pathname = useLocation({ select: (location) => location.pathname }); + const isLoading = useRouterState({ select: (s) => s.isLoading }); const mainContentId = useId(); const isMarketingPage = isStaticMarketingPath(pathname); return ( <> + {isLoading ? ( +