The previous install flow had a timing race: the `beforeinstallprompt` event fires at unpredictable times after page load (often ~30s+ into the session, when Chrome's heuristic finally decides the user is engaged). If the listener was attached only inside the dialog's onClick handler, by the time the user clicked OK the event had already fired into the void and the deferred prompt was null — so `install()` returned "unavailable" every time. The new flow attaches the listener as early as possible: 1. `pwaUtil.prepareInstallPrompt()` — new public method that calls the private `attachListener()`. Idempotent (guard via `listenersAttached`). 2. `pwaUtil.canPromptInstall()` — companion getter that returns whether a deferred event is currently cached. Useful for UI to decide whether to even show the install CTA. 3. `pwa-install-overlay.tsx` — calls `prepareInstallPrompt()` right after the isSupported / isInstalled guards. The overlay mounts when the user enters /chat, so this is the earliest practical point in the user flow. 4. `pwa-screen.tsx` — calls `prepareInstallPrompt()` BOTH at module load (synchronous, so the listener is attached before the splash screen's useEffects run) AND in a useEffect for robustness. The splash screen is the very first screen the user sees, so attaching here is the earliest possible. 5. `pwa-install-dialog.tsx` — `handleInstall` is now async, and `onInstall` accepts `() => void | Promise<void>`. The overlay calls `await pwaUtil.install()` so the deferred prompt flow can complete before the dialog unmounts. Together: the listener is now attached from the moment the splash screen module loads, the deferred prompt is captured whenever Chrome decides to fire the event, and the dialog's install button can reliably `prompt()` against the cached deferred. The full install flow now works end-to-end.
This is a Next.js project bootstrapped with create-next-app.
Getting Started
First, run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.
This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.
Git Hooks
项目使用 .githooks/ 目录存放仓库内的 git hooks(不在 .git/hooks/)。clone 后需手动启用:
git config core.hooksPath .githooks
启用的 Hook
post-receive
触发场景:git push 到本仓库时(在 bare repo 或 receive.denyCurrentBranch=updateInstead 的非 bare repo 上)。
行为:自动执行 pnpm run build:start,即 next build && next start。
注意事项:
- 本仓库的
.githooks/post-receive不会自动生效 —— 必须在每个工作副本上跑一次上面的git config命令 core.hooksPath是本地 git config(写入.git/config),不随仓库提交post-receive在接收端触发 —— 即 push 目标机器(生产 / 测试服务器)的 hook,不是本地开发机的
Learn More
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
Deploy on Vercel
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details. modify