refactor(home): split bootstrap, recovery polling, and shells
Move the home startup sequence and consultation recovery poll into plain functions, and the error and onboarding screens into components. Landing rules, the 20s fatal screen, retry, and the chart settings deep link stay the same.
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# PROGRESS · 首页 page.tsx 拆分(2026-09-25)
|
||||
|
||||
## 当前结论
|
||||
|
||||
T1、T2、T3、T5 完成。T4 让步到 1417 行(允许 ≤1450,未到 ≤1300)。没有新 BUG。没有推 `staging`,没有推 `main`。`?settings=chart` 仍打开星盘资料。
|
||||
|
||||
- 工作树:`G:\Ferti\Jyotisha\.worktrees\home-page-split-20260925`
|
||||
- 分支:`codex/home-page-split-20260925`
|
||||
- 开工基线:`origin/staging` = `59c23018`(与本分支当时一致)
|
||||
- 开工后 `origin/staging` 又前进了报告读者版 `3c2f7bd5` 和状态板 `9167d75e`。这两笔不碰首页。本分支没有把它们合进来,也没有快进 `staging`。
|
||||
- 实现提交短 SHA 写在随后的状态板提交里。
|
||||
|
||||
## 逐项
|
||||
|
||||
| 项 | 结果 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| T1 | 完成 | `runHomeBootstrap(deps, signal)` 在 `frontend/src/lib/home-bootstrap-run.ts`。内部再拆成 `runAccountPhase` / `resolveLanding`(注入项超过 15)。新文件 0 个 React hook。`Home()` 只留一个 effect 调用它。 |
|
||||
| T2 | 完成 | `runConsultationRecoveryPoll` 在 `frontend/src/lib/consultation-recovery-poll.ts`。假计时器覆盖 404 第一次只确认、下一次重放一次、第 4 次放弃、reserved 成功把缺失计数清零。 |
|
||||
| T3 | 完成 | `HomeBootstrapError`、`HomeOnboardingShell`。文案和按钮与拆分前一致。「重试」仍走 `retryHomeBootstrap(...reloadSessionList)`。 |
|
||||
| T4 | 让步 | `page.tsx` 1417 行(换行计数,与增长合同相同)。目标 ≤1300 未到,任务书允许先到 ≤1450。import 挤行已还原:`^import .*; import ` 对 `page.tsx` 为 0。增长合同 `PAGE_LINE_COUNT_BASELINE` 下调到 1417,上限 1567。`useState` 仍是 34(上限保持 36),`useRef` 仍是 37(上限保持 37),都没有增加。 |
|
||||
| T5 | 完成 | DESIGN 未改。CHANGELOG 一条「首页内部重构,无用户可见变化」。不新开 BUG。 |
|
||||
|
||||
## 还留在 Home() 里的大块
|
||||
|
||||
下一步若要压到 1300,还在首页里的是:账户弹窗模型(资料 / 星盘库 / 点数 / 退出)、对话区和 `ChatComposer` 装配、星盘库同步、今日星语、设置深链。设置深链必须留在首页,`query.pane === "chart"` 仍调用 `openAccountDialog("chart-library")`。
|
||||
|
||||
## 断言改写
|
||||
|
||||
| 原值 | 新值 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| `new-chat-recovery.test.ts` 把 page.tsx 激活段和 hook 的 restore 放进 vm,断言 active 等于预置本地会话 | 调用 `runHomeBootstrap`,restore 仍是 hook 里的真实函数;new-chat 会新建空咨询,恢复后仍停在这条新会话 | 激活段已搬进函数。直接调用还覆盖「新建落点与 reserved 恢复共存」,比只跑后半段更强 |
|
||||
| `consultation-recovery.test.ts` 按 page.tsx 恢复 effect 切片匹配 404 / 重放 / 第 4 次放弃 | 直接调用 `runConsultationRecoveryPoll`;逐步假计时器在 `consultation-recovery-poll.test.ts` | 轮询搬出后,源码切片不再落在 page.tsx |
|
||||
| 从 page.tsx 切 `storedPending` 分支,并用源码顺序断言它先于全局状态 | 调用 `runHomeBootstrap`:本页 pending 覆盖全局 reserved;无 pending 时不删 key 的反例由「有 pending 且 reserved 则不删 key」锁住。写入 `sessionId/requestId/question` 的 effect 仍留在 page.tsx,继续按该 effect 切片 | 启动流程搬走;优先级改由结果锁定,不弱于源码顺序 |
|
||||
| `homeSurface` 里预留判断、`window.setTimeout(() => void poll(), 1_750)`、完成后的 `fetchSessionDetail` | 预留判断在 `home-bootstrap-run.ts`;1750ms 和详情读取在 `consultation-recovery-poll.ts`,调用变成 `fetchDetail(deps.pendingSessionId, deps.modelCatalog, signal)` | 两段流程搬出首页。状态端点、间隔、完成后拉详情都还在 |
|
||||
| `restoreConsultationRecovery(recoverySession, reservedConsultation.requestId, storedPending)` 在 homeSurface | 同一调用在 `home-bootstrap-run.ts` | 启动恢复搬走,仍把 stored pending 交给真实 restore |
|
||||
| page.tsx 直接调用 `resolveStarterHomeLandingSessionId` / `starterHomeLandingNeedsConsultation`,并引用 `HOME_BOOTSTRAP_SLOW_MS` | 这两个调用和 20 秒预算在 `home-bootstrap-run.ts`;`shouldAutoOpenRectificationSession` 仍在 page.tsx | 落点和超时属于启动流程。揭幕阶段的校正会话判断留在首页 |
|
||||
| `if (bootstrapFailed) setHydrated(true); else setBootstrapPhase("prepare")` 在 page.tsx | 同一分支在 `runHomeBootstrap`,setter 经 `deps.commit` | 失败仍揭幕错误屏,成功仍进入 prepare,不半揭幕 |
|
||||
| 错误屏 `<main className="app-loading app-loading-error">` 在 page.tsx | 同一段在 `home-bootstrap-error.tsx`。加载中的 polite main 仍在 page.tsx | 错误屏抽成组件,class 与 `role="alert"` 不变 |
|
||||
| homeSurface 两处「回答已恢复,已显示在对话区末尾。」 | `use-consultation-run.ts` 与 `consultation-recovery-poll.ts` 各一处 | 轮询搬走,两处文案都还在 |
|
||||
| page.tsx 里 `clearStaleClientReload(sessionStorage)`,以及从 `loadCloudData` 切登录跳转不关超时 | 首页传入 `storage: sessionStorage`,函数内 `clearStaleClientReload(deps.storage)`;登录跳转的 try/finally 在 `home-bootstrap-run.ts`,仍是 `window.clearTimeout(bootstrapTimeout)` 且 `redirectedToLogin` 时提前 return | 清的仍是会话存储。登录跳转仍不取消 20 秒超时 |
|
||||
| 从 homeSurface 切 `async function loadCloudData()` | 切 `resolveLanding`。`defaultSessionId` 现为 `ready.nextSessions[0].id` | 落点代码原样搬家。清 new-chat URL 仍在 `setActiveSessionId` 之前,默认落点仍不 `push` |
|
||||
| 从 page.tsx 切 `if (previewMode)` 到 `await sessionListReady` | 预览分支在 `home-bootstrap-run.ts`,结束锚点是 `await deps.sessionListReady`。首页把 `sessionListReady` / `sessionListBoot` 传进去 | 预览仍不写会话 URL。`/` 仍不在首页里 `fetchSessions` |
|
||||
| `session-list-provider.test.ts` 匹配 page.tsx 的 `await sessionListReady;` | 匹配首页传入的 `sessionListReady`,以及函数里的 `await deps.sessionListReady` | 会话列表仍只由 provider 拉一次 |
|
||||
|
||||
## 验证
|
||||
|
||||
开工前,下面四份测试 `npx tsx --test`:**28 pass / 0 fail**。
|
||||
|
||||
- `frontend/tests/new-chat-recovery.test.ts`
|
||||
- `frontend/tests/consultation-recovery.test.ts`
|
||||
- `frontend/tests/home-shell-growth-contract.test.ts`
|
||||
- `frontend/tests/home-bootstrap-reveal.test.ts`
|
||||
|
||||
改完后同一组,加上本单新测试和会被搬家碰到的合同(`chat-session-url`、`stale-client-recovery`、`chat-navigation-a11y-contract`、`session-list-provider`、`home-bootstrap-run`、`consultation-recovery-poll`、`home-bootstrap-screens`、`settings-url`):**全部通过,没有新增失败**。最后一次合并跑是 81 pass / 0 fail;补上地点卡文案后又跑了 screens + settings-url,7 pass / 0 fail。增长合同与 `tsc --noEmit` 随后再跑,exit 0。
|
||||
|
||||
- `npx tsx --test` 使用明确文件路径。没有用全量 `npm test` 当信号。
|
||||
- `.\node_modules\.bin\tsc --noEmit`:0 错。
|
||||
- `npm run lint`:0 error,124 warnings。没有去清原本就未使用的 import,也没有为消 warning 改 effect 依赖。
|
||||
- `npm run build`:**没有通过**。Turbopack 拒绝本工作树的 `frontend/node_modules` junction(指向主检出,超出它认定的文件系统根)。按约束没有删除或重建这个 junction,也没有 `npm install`。因此不能声称 `/` 仍是 Static,也不能报首屏 gzip。
|
||||
|
||||
## 没做的验证
|
||||
|
||||
- 没有登录浏览器走首页。本单声称无用户可见变化,靠测试锁行为。
|
||||
- 没有 `next build`,见上。
|
||||
- 没有推 `origin/staging`,没有推 `main`,没有改 workflow,没有动 DNS。
|
||||
Reference in New Issue
Block a user