fix: give the points pill a ringed planet, not the 星盘 orbit
Independent Staging Quality Gate / validate (push) Successful in 18m37s
Independent Staging Quality Gate / publish (push) Successful in 4m30s

Orbit already marks 星盘 in the sidebar and the chat chart toggle, so on
phones two identical orbits sat side by side in the top bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
This commit is contained in:
2026-11-02 16:51:18 +08:00
co-authored by Claude Opus 5.5
parent 1cd73bc826
commit c8c08a49c6
5 changed files with 25 additions and 13 deletions
+1 -1
View File
@@ -20741,4 +20741,4 @@
- 防复发:上面的测试。规则:用 `:not(:has(...))` 隐藏一类元素时,要放出的例外必须写到同等或更高优先级,不能靠元素选择器。
- 相关记录:BUG-1470(顶栏高度与两行问题);同一变更是 2026-11-02 顶栏控件统一 44px 与选择星盘改版。
- 复发自:无
- 修复版本:分支 `codex/chart-polish-1102`(基于 `origin/staging` cb0a6289),待合并。
- 修复版本:提交 1cd73bc8(分支 `codex/chart-polish-1102`),2026-11-02 推上 staging。
@@ -1,13 +1,12 @@
"use client";
import { Orbit } from "lucide-react";
import { useEffect, useRef, useState, type MouseEvent } from "react";
import { Button } from "@/components/ui/button";
import { motionPresets } from "@/lib/motion/presets";
/**
* The top bar's points control (2026-11-02): an orbit, not a money sign, in the action colour,
* The top bar's points control (2026-11-02): a ringed planet, not a money sign, in the action colour,
* the balance, then 「点」. As tall as the person chip beside it (globals.css
* `--chat-header-pill`); the hit area is at least 44px.
*/
@@ -22,13 +21,23 @@ export function CreditBalanceButton({
}) {
return (
<Button variant="outline" className="credit-balance" type="button" aria-label={label} onClick={onClick}>
<Orbit className="credit-icon" aria-hidden="true" />
<RingedPlanet className="credit-icon" />
<RollingValue value={credits} />
<span className="credit-unit" aria-hidden="true">点</span>
</Button>
);
}
/** Lucide-style stroke icon. Not Lucide's Orbit: that one already means 「星盘」 in the sidebar and chat toggle. */
function RingedPlanet({ className }: { readonly className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="6" />
<path d="M7.5 8.6C4.2 7.2 1.9 7 1.5 8c-.6 1.5 3.6 4.8 9.4 7.2s11.1 3.3 11.6 1.8c.4-1-1.6-2.8-4.9-4.6" />
</svg>
);
}
function RollingValue({ value }: { readonly value: number | null }) {
const [roll, setRoll] = useState({ value, direction: 1 });
const nodeRef = useRef<HTMLSpanElement>(null);
@@ -113,9 +113,12 @@ test("the person chip and the points pill share one top-bar height token, deskto
assert.match(pointsBody, /display:\s*inline-flex/);
assert.match(pointsBody, /align-items:\s*center/);
assert.match(pointsBody, /border-radius:\s*999px/);
// No money sign on the points pill: an orbit stands in for the coin.
// No money sign on the points pill: a ringed planet stands in for the coin.
// 原值: <Orbit className="credit-icon" …/>; 新值: <RingedPlanet className="credit-icon" />;
// 原因: Orbit already marks 星盘 (sidebar, chat chart toggle), so two orbits sat side by side on phones.
const button = read("../src/components/credit-balance-button.tsx");
assert.match(button, /<Orbit className="credit-icon" aria-hidden="true" \/>/);
assert.match(button, /<RingedPlanet className="credit-icon" \/>/);
assert.doesNotMatch(button, /import \{[^}]*\bOrbit\b/);
assert.doesNotMatch(button, /CircleDollarSign|Coins|DollarSign/);
// Everything in the bar, the 44px sidebar toggle included, sits on the row's centre line.
assert.match(cssDeclarations(".chat-header", globalStyles), /align-items:\s*center/);
+3 -3
View File
@@ -20,9 +20,9 @@ test("the 2026-09-22 icon audit keeps the existing entry icons and adds no loop"
assert.doesNotMatch(starterHome, /Sparkles|thinking-orbs/);
assert.doesNotMatch(css, /\.starter-entry/);
const credits = readFileSync(new URL("../src/components/credit-balance-button.tsx", import.meta.url), "utf8");
// 原值(3): `<CircleDollarSign className="credit-icon" … />` 新值(3): `<Orbit className="credit-icon" … />`,仍无循环动画
// 原因(3): 2026-11-02 产品要求点数按钮不用钱币图标
assert.match(credits, /<Orbit className="credit-icon" aria-hidden="true" \/>/);
// 原值(3): `<CircleDollarSign className="credit-icon" … />` 新值(3): `<RingedPlanet className="credit-icon" />`(自绘带环行星),仍无循环动画
// 原因(3): 2026-11-02 产品要求点数按钮不用钱币图标、换成行星;Orbit 已是「星盘」入口图标,不复用
assert.match(credits, /<RingedPlanet className="credit-icon" \/>/);
assert.doesNotMatch(css.slice(css.indexOf(".credit-balance .credit-icon {"), css.indexOf("}", css.indexOf(".credit-balance .credit-icon {"))), /animation:/);
assert.doesNotMatch(css, /thinking-orbs/);
assert.match(css, /@media \(prefers-reduced-motion:\s*reduce\)[\s\S]*transition-duration:\s*\.01ms !important/);
@@ -123,10 +123,10 @@ test("D3: the points button is a single coin in the action colour and says how m
const credits = readFileSync(new URL("../src/components/credit-balance-button.tsx", import.meta.url), "utf8");
const css = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
// 原值(2):`import { CircleDollarSign }`、`<CircleDollarSign className="credit-icon" … />`(硬币)
// 新值(2):`import { Orbit }`、`<Orbit className="credit-icon" … />`,大小、主色、对齐不变
// 原因(2):2026-11-02 产品要求点数按钮不用钱币图标,改成像行星的 Orbit
assert.match(credits, /import \{ Orbit \} from "lucide-react";/);
assert.match(credits, /<Orbit className="credit-icon" aria-hidden="true" \/>/);
// 新值(2):自绘 `<RingedPlanet className="credit-icon" />`(带环行星),大小、主色、对齐不变
// 原因(2):2026-11-02 产品要求点数按钮不用钱币图标、换成行星;Lucide 的 Orbit 已是「星盘」入口图标,不复用
assert.match(credits, /function RingedPlanet\(/);
assert.match(credits, /<RingedPlanet className="credit-icon" \/>/);
assert.doesNotMatch(credits, /CircleDollarSign|Coins|DollarSign/);
assert.match(credits, /<span className="credit-unit" aria-hidden="true">点<\/span>/);
assert.match(css, /\.credit-balance \.credit-icon \{[^}]*color: var\(--color-action\);/);