启程清单、提示和动效改到页面出来后再载入。

首屏 gzip-9 从比基线多 11.05% 降到多 7.54%。首页仍是静态页。加载时不转圈。

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-27 02:33:04 +08:00
co-authored by Grok (agent loop)
parent 6e01889b34
commit 4fcade7238
6 changed files with 40 additions and 13 deletions
+6
View File
@@ -1,5 +1,11 @@
# 印度占星 Skill 更新日志
## 2026-10-27 — 启程清单、提示和动效不进首屏
- 首页先出来。启程清单、一次性提示和启程动效随后再载入,中间不转圈、不写「正在加载」。
- 动效还没到手时,内容保持可见。
- Skill 版本不变。
## 2026-10-25 — 组件库收尾:去掉没人用的旧样式
- 删掉样式表里已经没有页面使用的旧类。还在用的类和动画都留着。
@@ -154,14 +154,12 @@
## 首屏体积(U8)
这次构建是在 `521e902f3` 上跑的。`npm run build` 打完路由表,`/` 是 `○ Static`。口径:`.next/server/app/index.html` 引用的 `/_next/static/*.js` 和 `*.css`,去重后每个用 gzip -9,字节相加。
口径:`npm run build` 之后,`.next/server/app/index.html` 引用的 `/_next/static/*.js` 和 `*.css`,去重后每个用 gzip -9,字节相加。基线是 `0f66d92d4`,在另一棵工作树里用当时的锁文件 `npm ci` 再构建。两次构建的 `/` 都是 `○ Static`。`skill-package-registry.ts` 的 5 条追踪警告两边都有,这次没改那份文件,也没改 next 配置。
| 项 | 字节 |
| --- | --- |
| 文件数 | 39,缺失 0 |
| gzip-9 合计 | 717631 |
| 其中 js | 636112 |
| 其中 css | 81519 |
| 未压缩合计 | 2460717 |
| 版本 | 文件 | gzip-9 | js | css | 相对基线 |
| --- | --- | --- | --- | --- | --- |
| 基线 `0f66d92d4` | 36,缺失 0 | 646201 | 575350 | 70851 | — |
| 延后加载之前 | 39,缺失 0 | 717631 | 636112 | 81519 | +71430,+11.05% |
| 延后加载之后 | 38,缺失 0 | 694941 | 613422 | 81519 | +48740,+7.54% |
基线 `0f66d92d4` 用同一口径另开工作树再量。超过 ±2% 才把启程清单、提示层、动效改成用到时再加载。构建日志里 `skill-package-registry.ts` 的追踪警告这次没改那份文件,也没改 next 配置。
延后加载之前那次构建在 `521e902f3`。超过 2% 之后,启程清单、一次性提示和启程动效宿主改成页面出来后再载入,不占位、不转圈。这一步少了 22690 字节。相对基线仍是 +7.54%,还在 2% 外面。多出来的是组件库外壳和整站样式,不是这三块还留在首屏里。
+1 -1
View File
@@ -1283,7 +1283,7 @@ Yogini 还在取时,右栏用和 Chara 相同的轨道等待(`OrbitWaiting`
一次性提示用现有弹出层,宽 `min(18rem, calc(100vw - 2rem))`,不抢焦点。侧栏两项从锚点右侧伸出,其余从上方伸出,间距 8px。按钮是「知道了」。同一时刻最多一条。天空过场、兑换窗、任何弹窗或抽屉、启程清单正打开、回答还在生成时,提示不出;挡着的那一条等条件过去再出。侧栏「星盘」、手机菜单和星盘四个页签上的小圆点是 8px,贴在右上角 4px,不是字符。
启程专用动效只在启程里播放:清单还开着且有未完成项,或账户还没完成第一问。清单已经关掉的账户不播追问进场、档案卡进场、星盘和大运画出、合盘头像靠拢。动效代码还没到手、或 300ms 内没到手时,内容保持可见,不先藏起来。只在元素第一次出现时播放。重绘、来回切换不重播。除回答流式中的呼吸点外,没有无限循环。只动 `opacity`、`transform`、颜色、`clip-path`,不触发布局。Motion 用到才加载,不进首屏。系统开启「减少动态效果」时,星点和圆点闪烁去掉,其余只淡入,没有位移、缩放和擦除。
启程专用动效只在启程里播放:清单还开着且有未完成项,或账户还没完成第一问。清单已经关掉的账户不播追问进场、档案卡进场、星盘和大运画出、合盘头像靠拢。动效代码还没到手、或 300ms 内没到手时,内容保持可见,不先藏起来。只在元素第一次出现时播放。重绘、来回切换不重播。除回答流式中的呼吸点外,没有无限循环。只动 `opacity`、`transform`、颜色、`clip-path`,不触发布局。Motion 用到才加载,不进首屏。启程清单、一次性提示和启程动效宿主也在页面出来后再载入,不进首屏。没载入之前不占位、不转圈;内容保持可见。系统开启「减少动态效果」时,星点和圆点闪烁去掉,其余只淡入,没有位移、缩放和擦除。
| 类别 | 用在哪 | 时长 | 曲线 |
| --- | --- | --- | --- |
+11 -2
View File
@@ -1,11 +1,10 @@
"use client";
import type { ReactNode } from "react";
import dynamic from "next/dynamic";
import { AccountDeletionGate } from "@/components/account-deletion-gate";
import { AppSidebar } from "@/components/app-sidebar";
import { OnboardingHintLayer } from "@/components/onboarding-hint-layer";
import { OnboardingMotionHost } from "@/components/onboarding-motion-host";
import { LegalConsentGate } from "@/components/legal/legal-consent-gate";
import { PageSwitch } from "@/components/shell-motion";
import { ShellFrame } from "@/components/shell-frame";
@@ -14,6 +13,16 @@ import { OnboardingJourneyProvider } from "@/lib/onboarding-journey";
import { SessionListProvider, sessionListSidebarModel, useSessionList, useShellRegistration } from "@/hooks/use-session-list";
import "../site-styles";
// After the page is up. No placeholder: a spinner is not allowed once the home screen is revealed.
const OnboardingHintLayer = dynamic(
() => import("@/components/onboarding-hint-layer").then((module) => module.OnboardingHintLayer),
{ ssr: false },
);
const OnboardingMotionHost = dynamic(
() => import("@/components/onboarding-motion-host").then((module) => module.OnboardingMotionHost),
{ ssr: false },
);
function AppShell({ children }: { children: ReactNode }) {
const list = useSessionList();
const registration = useShellRegistration();
+6 -1
View File
@@ -51,7 +51,6 @@ import { notifyOnboardingDailyOpened } from "@/lib/onboarding-signals";
import { personRelationLabel } from "@/lib/person-relation";
import { readStarterSignMap, readStarterSigns, subscribeStarterSigns } from "@/lib/starter-questions";
import { DailyStarlanguageBinder } from "@/components/daily-starlanguage-binder";
import { OnboardingChecklistEntry } from "@/components/onboarding-checklist";
import { OnboardingChartDot } from "@/components/onboarding-dot";
import { OnboardingHomeBridge } from "@/components/onboarding-home-bridge";
import { AccountDialogDeepLink, SynastryDeepLink } from "@/components/people-home-links";
@@ -201,6 +200,12 @@ const BillingPanel = dynamic(loadBillingPanel, {
loading: () => <p className="secondary-page-waiting">账户信息还没拿到。</p>,
});
// After the page is up. No placeholder: a spinner is not allowed once the home screen is revealed.
const OnboardingChecklistEntry = dynamic(
() => import("@/components/onboarding-checklist").then((module) => module.OnboardingChecklistEntry),
{ ssr: false },
);
let nudgeComposerTimer: number | undefined;
function clearNudgeComposerTimer() {
@@ -282,6 +282,15 @@ test("checklist and hint wiring stay inside the existing shell", () => {
const home = page.slice(page.indexOf("export default function Home("));
assert.match(page, /<SidebarTrigger placement="inset" \/>/);
assert.match(page, /<OnboardingChecklistEntry \/>/);
// 原值:首页静态引入启程清单。
// 新值:清单、提示层、动效宿主都是页面出来后再载入,且不给占位。
// 原因:首屏 gzip-9 比基线 0f66d92d4 大 11.05%,任务书要求这三块不进首屏。
assert.match(page, /import\("@\/components\/onboarding-checklist"\)[\s\S]{0,200}ssr: false/);
assert.doesNotMatch(page, /import \{ OnboardingChecklistEntry \}/);
assert.match(layout, /import\("@\/components\/onboarding-hint-layer"\)[\s\S]{0,200}ssr: false/);
assert.match(layout, /import\("@\/components\/onboarding-motion-host"\)[\s\S]{0,200}ssr: false/);
assert.doesNotMatch(layout, /import \{ OnboardingHintLayer \}/);
assert.doesNotMatch(layout, /import \{ OnboardingMotionHost \}/);
const header = page.slice(page.indexOf('<header className="chat-header">'), page.indexOf("composer-wrap"));
assert.match(header, /<OnboardingChecklistEntry \/>/);
assert.equal(header.includes("composer-wrap"), false);