From 689e01296e1e9a0c4fd01baf0c7f27357af662e8 Mon Sep 17 00:00:00 2001
From: jesse-ux
Date: Sun, 25 Oct 2026 06:56:39 +0800
Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=BA=93=E6=94=B6=E5=B0=BE?=
=?UTF-8?q?=EF=BC=9A=E5=88=A0=E6=8E=89=E6=B2=A1=E4=BA=BA=E7=94=A8=E7=9A=84?=
=?UTF-8?q?=E6=97=A7=E6=A0=B7=E5=BC=8F=EF=BC=8C=E6=A3=80=E6=9F=A5=E5=90=8D?=
=?UTF-8?q?=E5=8D=95=E5=8F=AA=E7=95=99=E7=BB=98=E5=88=B6=E4=BE=8B=E5=A4=96?=
=?UTF-8?q?=EF=BC=88#111=EF=BC=89=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
globals.css 从 7249 行减到 7011 行,删了 56 个已经没人用的类,16 个关键帧都还在用。手写控件名单从 9 个文件收到 2 个:天空绘制和校正顶栏探头。星盘和星图的图标豁免仍在规则里。设计说明改成以 shadcn base-nova 和 Motion 预设为准。页面用法没有再改。
自测:tsc --noEmit 退出码 0;frontend 下 npm run lint 0 error、122 warning;白名单、样式隔离、时间线和错误边界测试 18 条通过、失败 0、取消 0;npm run build 后 / 仍是 Static。首屏 JS gzip-9 相对 728ead88 从 644683 到 648993(+0.67%)。没有跑全量 npm test、Python 快速门和 test:db。
Co-Authored-By: Grok (agent loop)
---
CHANGELOG.md | 7 +
docs/BUG_HISTORY.md | 6 +-
.../PROGRESS-ui-kit-7-cleanup-20261023.md | 62 ++++
docs/tasks/README.md | 2 +-
docs/testing/ui-kit-migration-20261023.md | 10 +
frontend/DESIGN.md | 348 +++++++++---------
frontend/docs/VOICE.md | 2 +-
frontend/eslint/no-handwritten-control.mjs | 5 +
frontend/eslint/ui-primitive-whitelist.json | 7 -
frontend/src/app/error.tsx | 6 +-
frontend/src/app/globals.css | 316 ++--------------
.../components/ayanamsa-preference-field.tsx | 9 +-
.../src/components/birth-place-picker.tsx | 5 +-
.../components/consultation-run-timeline.tsx | 8 +-
.../src/components/home-bootstrap-error.tsx | 4 +-
.../src/components/home-onboarding-shell.tsx | 5 +-
.../personal-report-markdown-view.tsx | 5 +-
.../tests/chat-stream-settle-contract.test.ts | 5 +-
.../site-style-isolation-contract.test.ts | 9 +-
frontend/tests/ui-kit-whitelist.test.ts | 69 +---
20 files changed, 352 insertions(+), 538 deletions(-)
create mode 100644 docs/tasks/PROGRESS-ui-kit-7-cleanup-20261023.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
index af8f857e..aeeddf55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# 印度占星 Skill 更新日志
+## 2026-10-25 — 组件库收尾:去掉没人用的旧样式
+
+- 删掉样式表里已经没有页面使用的旧类。还在用的类和动画都留着。
+- 手写控件的检查名单收到只剩两处:那一刻的天空绘制,以及校正盘面挂在顶栏的探头。星盘和星图的画法仍可例外。
+- 设计说明改成以组件库为准。页面上的按钮、文案和操作没有再改一轮。
+- 没有新的故障。Skill 版本不变。
+
## 2026-10-25 — 生时校正的选项卡片换成组件库
- 校正里的选项仍是圆角卡片。点中之后是浅底描边。确定程度仍是三个圆点,大概范围仍是一排标签。
diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md
index 409be2db..3fe019d0 100644
--- a/docs/BUG_HISTORY.md
+++ b/docs/BUG_HISTORY.md
@@ -19370,14 +19370,14 @@
- 状态:enhancement
- 首次发现:2026-10-24
-- 最近更新:2026-10-24
-- 影响面:前端依赖、对话消息入场、设计令牌别名、新增的 shadcn 组件、手写控件检查。侧栏、对话页、星盘页的控件这一张没有换。
+- 最近更新:2026-10-25。K7 把白名单从迁完后剩下的 9 个文件收到 2 个。没有新的故障现象,不另编号。
+- 影响面:前端依赖、对话消息入场、设计令牌别名、新增的 shadcn 组件、手写控件检查。侧栏、对话页、星盘页的控件这一张没有换。K7 只删没用的旧样式,并换掉还留在名单里的普通按钮。
- 用户现象:页面上的控件和文案与改前相同。对话新出现的一行仍是一次短入场;系统开了「减少动态效果」时不做这次入场。
- 触发条件:打开首页或已有对话。这一张不改变请求、扣点或落库。
- 根因:不是缺陷。产品在组件库迁移里定了换用 Motion、shadcn、按需加载和 lint 拦截(D1、D2、D3、D4、D7)。这一张只加底座。
- 修复:去掉 GSAP。消息入场改为按需加载的 Motion,时长仍是 160 毫秒。用固定版本的 shadcn CLI 补上任务书列出的组件,不重装已有的 8 个。设计令牌映射到 shadcn 变量,深色浅色都跟着现有色板。Motion 预设和四个 store 只是骨架,没有接到页面上。`src/components/ui` 以外的新手写控件会报错;迁移前已有的文件在白名单里,测试锁住这份名单不能变长。星盘与星图的 SVG 绘制路径豁免图标规则。
- 验证:见 `docs/tasks/PROGRESS-ui-kit-0-base-20261023.md`。没有浏览器登录态,不把界面截图写成通过。
-- 防复发:`frontend/tests/ui-kit-whitelist.test.ts` 锁住白名单只减不增,并检查新文件里的 `
);
})}
diff --git a/frontend/src/components/birth-place-picker.tsx b/frontend/src/components/birth-place-picker.tsx
index a579ee75..73c0e26d 100644
--- a/frontend/src/components/birth-place-picker.tsx
+++ b/frontend/src/components/birth-place-picker.tsx
@@ -3,6 +3,7 @@
import { Check, MapPin, RotateCcw } from "lucide-react";
import { InlineSpinner } from "@/components/inline-spinner";
import { useEffect, useId, useRef, useState } from "react";
+import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import {
birthPlaceDraftFromLevels,
@@ -487,9 +488,9 @@ export function BirthPlacePicker({
{(settled.status === "idle" || settled.status === "error") && }
{statusText}
{settled.status === "error" && (
- setRetryToken((token) => token + 1)}>
+ setRetryToken((token) => token + 1)}>
重试
-
+
)}
diff --git a/frontend/src/components/consultation-run-timeline.tsx b/frontend/src/components/consultation-run-timeline.tsx
index bcc43d0e..cf23b0fe 100644
--- a/frontend/src/components/consultation-run-timeline.tsx
+++ b/frontend/src/components/consultation-run-timeline.tsx
@@ -4,6 +4,7 @@ import { useId, useState } from "react";
import { BookOpen, Check, ChevronDown, Layers, ListTodo, LoaderCircle, PenLine, type LucideIcon } from "lucide-react";
import { InlineSpinner } from "@/components/inline-spinner";
+import { Button } from "@/components/ui/button";
import { CONSULTATION_RECEIVED_LABEL } from "@/lib/consultation-activity-labels";
void LoaderCircle; // source lock: tests/chat-stream-layout.test.ts matches /LoaderCircle/
@@ -82,9 +83,10 @@ export function ConsultationRunTimeline({
className={`consultation-run-timeline${open ? " is-open" : ""}${live ? " is-live" : ""}`}
aria-label="分析步骤"
>
- setUserOpen(!open)}
@@ -96,7 +98,7 @@ export function ConsultationRunTimeline({
{summary}
-
+
diff --git a/frontend/src/components/home-bootstrap-error.tsx b/frontend/src/components/home-bootstrap-error.tsx
index 07c09e6d..78fb2af0 100644
--- a/frontend/src/components/home-bootstrap-error.tsx
+++ b/frontend/src/components/home-bootstrap-error.tsx
@@ -2,6 +2,8 @@
import Link from "next/link";
+import { Button } from "@/components/ui/button";
+
export function HomeBootstrapError({
accountError,
onRetry,
@@ -15,7 +17,7 @@ export function HomeBootstrapError({
暂时无法进入 Jyotisha
{accountError}
- 重试
+ 重试
返回登录
diff --git a/frontend/src/components/home-onboarding-shell.tsx b/frontend/src/components/home-onboarding-shell.tsx
index 98005727..b3767d59 100644
--- a/frontend/src/components/home-onboarding-shell.tsx
+++ b/frontend/src/components/home-onboarding-shell.tsx
@@ -7,6 +7,7 @@ import { BirthTimeIntakeFields } from "@/components/birth-time-intake";
import { rememberSkyOrigin } from "@/components/birth-sky/sky-origin";
import { OnboardingChatMessage } from "@/components/onboarding-chat-message";
import { OnboardingWhoStep } from "@/components/onboarding-who-step";
+import { Button } from "@/components/ui/button";
import type { BirthTimeGuidedController } from "@/hooks/use-birth-time-guided-journey";
import {
applyBirthTimeDraftPatch,
@@ -117,7 +118,7 @@ export function HomeOnboardingShell({
setProfileDraft((current) => applyBirthTimeDraftPatch(current, patch))} />
{birthTimeContinueHint ?
{birthTimeContinueHint}
:
}
-
{profileSaving ? "保存中" : "继续"}
+
{profileSaving ? "保存中" : "继续"}
@@ -133,7 +134,7 @@ export function HomeOnboardingShell({
出生地点搜索全球城市或区县,系统会匹配出生当日的时区。
diff --git a/frontend/src/components/personal-report/personal-report-markdown-view.tsx b/frontend/src/components/personal-report/personal-report-markdown-view.tsx
index 2eb9888f..12e49efd 100644
--- a/frontend/src/components/personal-report/personal-report-markdown-view.tsx
+++ b/frontend/src/components/personal-report/personal-report-markdown-view.tsx
@@ -17,6 +17,7 @@ import { parseReportFocusBlock, reportTitleByline } from "@/lib/report-focus-blo
import { reportChartGrid } from "@/lib/report-chart-grid-rehype";
import { NorthIndianChartSvg } from "@/components/personal-report/vedic-chart-svg";
import { ReportEdgeJump } from "@/components/personal-report/report-edge-jump";
+import { Button } from "@/components/ui/button";
import type { ReportLanguage } from "@/lib/report-language";
import {
buildLongformOutline,
@@ -381,9 +382,9 @@ export function PersonalReportMarkdownView({
) : null}
diff --git a/frontend/tests/chat-stream-settle-contract.test.ts b/frontend/tests/chat-stream-settle-contract.test.ts
index 046a336b..45912df0 100644
--- a/frontend/tests/chat-stream-settle-contract.test.ts
+++ b/frontend/tests/chat-stream-settle-contract.test.ts
@@ -54,7 +54,10 @@ test("the timeline collapses in place with a 180ms height transition and honours
assert.match(timelineSource, /const open = userOpen \?\? live/);
assert.match(timelineSource, /aria-expanded=\{open\}/);
assert.match(timelineSource, /inert=\{open \? undefined : true\}/);
- assert.match(timelineSource, /className="consultation-run-timeline__summary"/);
+ // 原值:className="consultation-run-timeline__summary"
+ // 新值:同一类名仍在,后面可以再接压掉按钮默认高度和换行的类
+ // 原因:K7 把这一行换成组件库 Button。类名、展开和 180ms 高度过渡不变
+ assert.match(timelineSource, /className="consultation-run-timeline__summary(?: [^"]*)?"/);
assert.match(timelineSource, /agent-activity-status__text/);
assert.match(globalStyles, /\.consultation-run-timeline__body-wrap \{[^}]*grid-template-rows: 0fr/);
assert.match(globalStyles, /\.consultation-run-timeline__body-wrap \{[^}]*transition: grid-template-rows 180ms var\(--ease-out\)/);
diff --git a/frontend/tests/site-style-isolation-contract.test.ts b/frontend/tests/site-style-isolation-contract.test.ts
index e4dc034c..99c501eb 100644
--- a/frontend/tests/site-style-isolation-contract.test.ts
+++ b/frontend/tests/site-style-isolation-contract.test.ts
@@ -82,13 +82,18 @@ test("site chrome owns globals.css; admin does not import it", () => {
/--font-body: var\(--font-inter, Inter\)/);
});
-test("root error and not-found pages do not depend on Tailwind utilities or Button", () => {
+test("root error and not-found pages do not depend on the site stylesheet", () => {
const errorPage = readFileSync(new URL("../src/app/error.tsx", import.meta.url), "utf8");
const notFoundPage = readFileSync(new URL("../src/app/not-found.tsx", import.meta.url), "utf8");
+ // 原值:error.tsx 与 not-found 都不得 import Button
+ // 新值:error.tsx 可以 import Button,颜色仍写在内联样式里;not-found 仍不得 import Button
+ // 原因:K7 清空白名单。这一页不引进整站样式表,避免聊天样式进后台路由
for (const source of [errorPage, notFoundPage]) {
assert.doesNotMatch(source, /className="flex/);
assert.doesNotMatch(source, /min-h-svh/);
assert.doesNotMatch(source, /text-muted-foreground/);
- assert.doesNotMatch(source, /@\/components\/ui\/button/);
+ assert.doesNotMatch(source, /site-styles|globals\.css/);
}
+ assert.match(errorPage, /import \{ Button \} from "@\/components\/ui\/button";/);
+ assert.doesNotMatch(notFoundPage, /@\/components\/ui\/button/);
});
diff --git a/frontend/tests/ui-kit-whitelist.test.ts b/frontend/tests/ui-kit-whitelist.test.ts
index 22c46d31..feb2557b 100644
--- a/frontend/tests/ui-kit-whitelist.test.ts
+++ b/frontend/tests/ui-kit-whitelist.test.ts
@@ -5,70 +5,17 @@ import { fileURLToPath } from "node:url";
import { ESLint } from "eslint";
-/** K0 snapshot. Later issues may delete entries. Adding one fails this test. */
+/**
+ * K7 ceiling. The list may still shrink. Adding one fails this test.
+ * 原值:K0 的 62 个文件。
+ * 新值:天空绘制和校正顶栏探头这两个文件。
+ * 原因:K7 清空白名单。星盘和星图的图标豁免在规则里,不在这份名单。
+ * 天空过场必须留着原生 dialog.showModal 和 createPortal,合同测试锁住这段,换组件会改绘制。
+ * 校正探头必须留着 createPortal 挂到顶栏,合同测试锁住这句;改成弹窗会改 K6 的交互。
+ */
const BASELINE = [
- "src/app/(app)/page.tsx",
- "src/app/error.tsx",
- "src/app/global-error.tsx",
- "src/components/account-deletion-gate.tsx",
- "src/components/account-deletion-section.tsx",
- "src/components/account-dialog-overlay.tsx",
- "src/components/ayanamsa-preference-field.tsx",
- "src/components/billing-panel.tsx",
- "src/components/birth-place-picker.tsx",
"src/components/birth-sky/birth-sky-converge.tsx",
- "src/components/birth-sky/birth-sky-replay.tsx",
- "src/components/birth-time-candidate-result.tsx",
- "src/components/birth-time-choice-question.tsx",
- "src/components/birth-time-evidence-draft-card.tsx",
- "src/components/birth-time-guide-turn.tsx",
- "src/components/birth-time-intake.tsx",
- "src/components/birth-time-legacy-rectification.tsx",
- "src/components/birth-time-rectification.tsx",
- "src/components/chart-page/chart-birth-editor.tsx",
- "src/components/chart-page/chart-dasha-tab.tsx",
- "src/components/chart-page/chart-page-view.tsx",
- "src/components/chart-page/chart-type-picker.tsx",
- "src/components/chart-page/chart-vedic-tab.tsx",
- "src/components/chart-profile-form.tsx",
- "src/components/chat-composer.tsx",
- "src/components/chat-message-actions.tsx",
- "src/components/consultation-run-timeline.tsx",
- "src/components/conversation-follow-ups.tsx",
- "src/components/email-otp-login.tsx",
- "src/components/ephemeris/ephemeris-page.tsx",
- "src/components/feedback-dialog.tsx",
- "src/components/home-bootstrap-error.tsx",
- "src/components/home-onboarding-shell.tsx",
- "src/components/jump-to-latest-button.tsx",
- "src/components/legal/legal-consent-gate.tsx",
- "src/components/legal/login-consent.tsx",
- "src/components/markdown-code-block.tsx",
- "src/components/model-selector.tsx",
- "src/components/onboarding-redeem-paywall.tsx",
- "src/components/people/people-page.tsx",
- "src/components/person-relation-field.tsx",
- "src/components/personal-report/personal-report-center.tsx",
- "src/components/personal-report/personal-report-markdown-view.tsx",
- "src/components/personal-report/report-actions.tsx",
- "src/components/personal-report/report-edge-jump.tsx",
- "src/components/personal-report/report-export-drawer.tsx",
- "src/components/personal-report/report-row-delete.tsx",
- "src/components/profile-gender-field.tsx",
- "src/components/profile-panel.tsx",
"src/components/rectification-agentic-chat.tsx",
- "src/components/rectification-board.tsx",
- "src/components/rectification-choice-card.tsx",
- "src/components/rectification-composer-reopen.tsx",
- "src/components/rectification-range-delivery.tsx",
- "src/components/rectification-segment-delivery.tsx",
- "src/components/reply-down-reason.tsx",
- "src/components/starter-home.tsx",
- "src/components/subject-switcher.tsx",
- "src/components/synastry/synastry-add-dialog.tsx",
- "src/components/synastry/synastry-picker.tsx",
- "src/components/synastry/synastry-report-view.tsx",
- "src/components/theme-preference-menu.tsx",
] as const;
const RULE = "jyotisha-ui/no-handwritten-control";