fix(effort): 波纹 v3 — 去黑边 + 删中心高频涟漪 + y 轴覆盖快捷键行

用户反馈三个问题:
1. "黑色边感觉不太对" — 最暗档 #0a0d1a (rgb 10,13,26) 太接近纯黑,
   远端波谷看起来像硬黑边。改为 #1a1f3a (rgb 26,31,58),紫蓝感
   更强而非纯黑。
2. "中心的快速波纹有点奇怪" — 删除震源附近 dist<6 的高频涟漪叠加
   (time*0.02,5 倍主波纹频率)。原本想让震源附近"水波感"更强,
   实际效果像"快速闪烁"反而突兀。主波纹已经足够,无需叠加。
3. "y 方向覆盖快捷键" — RippleContent 新增 y=2 行渲染快捷键 overlay
   ("←/→ adjust · Enter confirm · Esc cancel")。PlainContent 路径
   保持原 Box marginTop=1 + Text 渲染。

色阶调整(紫蓝感更强):
- #1a1f3a (原 #0a0d1a) — 最暗档
- #1f2543 / #252c55 / #2e3870 / #3a4582 / #4a5bb0 / #5769F7
  (中间档略调亮度,保持平滑过渡)

测试:震源点测试更新为"time=0 时波谷最暗,time 推进后扫过波峰变亮",
反映删除高频涟漪后的纯主波纹行为。

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
This commit is contained in:
claude-code-best
2026-06-14 15:25:00 +08:00
parent ac348a2b6a
commit 490714dbcb
3 changed files with 46 additions and 30 deletions

View File

@@ -123,10 +123,16 @@ export function EffortPanel({ appStateEffort, onDone }: Props): React.ReactNode
Effort
</Text>
{envActive && <Text color="warning">{`⚠ CLAUDE_CODE_EFFORT_LEVEL=${envRaw} overrides this session`}</Text>}
{rippleActive ? <RippleContent renderRow={renderRippleRow} cursor={cursor} /> : <PlainContent cursor={cursor} />}
<Box marginTop={1}>
<Text color="subtle">/ adjust · Enter confirm · Esc cancel</Text>
</Box>
{rippleActive ? (
<RippleContent renderRow={renderRippleRow} cursor={cursor} />
) : (
<>
<PlainContent cursor={cursor} />
<Box marginTop={1}>
<Text color="subtle">/ adjust · Enter confirm · Esc cancel</Text>
</Box>
</>
)}
</Box>
);
}
@@ -218,6 +224,11 @@ function RippleContent({ renderRow, cursor }: RippleContentProps): React.ReactNo
x: segmentTextStartX(cursorIdx, SUBLABEL_ULTRACODE.length),
color: COLOR_LABEL_DEFAULT,
};
const hintOverlay: Overlay = {
text: '←/→ adjust · Enter confirm · Esc cancel',
x: 0,
color: COLOR_LABEL_DEFAULT,
};
// 各行 y 坐标(相对震源 RIPPLE_SOURCE_Y = 档位名行)
// y=-3: Faster/Smarter
@@ -225,6 +236,7 @@ function RippleContent({ renderRow, cursor }: RippleContentProps): React.ReactNo
// y=-1: ▲
// y=0: 档位名(震源)
// y=1: 副标签
// y=2: 快捷键行y 方向延伸覆盖到底部)
return (
<>
<RippleRow segments={renderRow(-3, [fasterOverlay, smarterOverlay])} />
@@ -232,6 +244,7 @@ function RippleContent({ renderRow, cursor }: RippleContentProps): React.ReactNo
<RippleRow segments={renderRow(-1, [cursorOverlay])} />
<RippleRow segments={renderRow(0, labelOverlays)} />
<RippleRow segments={renderRow(1, [sublabelOverlay])} />
<RippleRow segments={renderRow(2, [hintOverlay])} />
</>
);
}

View File

@@ -11,11 +11,11 @@ import {
describe('intensityToColor', () => {
test('intensity=0 → 最暗档(不再是 transparent作面板底色', () => {
expect(intensityToColor(0)).toBe('#0a0d1a')
expect(intensityToColor(0)).toBe('#1a1f3a')
})
test('intensity < 0 钳到 0 → 最暗档', () => {
expect(intensityToColor(-0.5)).toBe('#0a0d1a')
expect(intensityToColor(-0.5)).toBe('#1a1f3a')
})
test('intensity > 0 → 永远是 #hex 颜色字符串(不返回 transparent', () => {
@@ -95,17 +95,26 @@ describe('computeRippleCells', () => {
).toEqual([])
})
test('震源点处颜色为最亮档dist=0falloff=1intensity 高)', () => {
const cells = computeRippleCells({
test('震源点 time=0 时为波谷最暗档time 推进后出现亮档', () => {
// dist=0time=0 时 phase = -0 = 0sin(0)=0 → wave=0 → intensity=0 → 最暗档
const t0 = computeRippleCells({
y: 5,
width: 11,
time: 0,
sourceX: 5,
sourceY: 5,
})
// 震源在 (5,5)dist=0falloff=1dist<6 触发高频涟漪叠加
// 波峰附近颜色应较高档(非最暗)
expect(cells[5].color).not.toBe('#0a0d1a')
expect(t0[5].color).toBe('#1a1f3a')
// time 推进phase 变化,震源会扫过波峰
const t1 = computeRippleCells({
y: 5,
width: 11,
time: 1500,
sourceX: 5,
sourceY: 5,
})
expect(t1[5].color).not.toBe('#1a1f3a')
})
test('覆盖半径扩大dist=65左侧远端仍有非最暗颜色', () => {
@@ -132,7 +141,7 @@ describe('computeRippleCells', () => {
sourceX: 65,
sourceY: 0,
})
const nonDarkest = t1.filter(c => c.color !== '#0a0d1a')
const nonDarkest = t1.filter(c => c.color !== '#1a1f3a')
expect(nonDarkest.length).toBeGreaterThan(0)
})

View File

@@ -20,14 +20,15 @@
* "暗紫蓝海洋"作为底色,波峰在底色上流动。这样波纹颜色变化更明显,
* 波谷也有暗色(不会"消失")。
*
* 波峰最高升到 suggestion (#5769F7),避免与文字 overlay也用 suggestion 系)
* 同色互相吞噬。文字用更亮的高光色(#a3b5ff保持对比。
* 最暗档用 #1a1f3a紫黑亮度 ~12%),不是纯黑——避免远端波谷
* 看起来像"硬黑边"。波峰最高升到 suggestion (#5769F7),避免与
* 文字 overlay也用 suggestion 系)同色互相吞噬。
*/
const RIPPLE_COLOR_STOPS = [
'#0a0d1a', // 0.00 ~ 0.14 — 最暗,波谷底色
'#15182b', // 0.14 ~ 0.28
'#1f2543', // 0.28 ~ 0.42
'#2a3360', // 0.42 ~ 0.56
'#1a1f3a', // 0.00 ~ 0.14 — 最暗(紫黑底色,非纯黑)
'#1f2543', // 0.14 ~ 0.28
'#252c55', // 0.28 ~ 0.42
'#2e3870', // 0.42 ~ 0.56
'#3a4582', // 0.56 ~ 0.70
'#4a5bb0', // 0.70 ~ 0.84
'#5769F7', // 0.84 ~ 1.00 — suggestion (波峰)
@@ -37,7 +38,7 @@ const RIPPLE_COLOR_STOPS = [
* 强度(任意实数)→ 颜色字符串。
*
* 钳到 [0, 1],按 RIPPLE_COLOR_STOPS 分级。永不返回 transparent。
* intensity=0 → 最暗档(#0a0d1a作为面板底色
* intensity=0 → 最暗档(#1a1f3a作为面板底色
*/
export function intensityToColor(intensity: number): string {
const v = intensity < 0 ? 0 : intensity > 1 ? 1 : intensity
@@ -98,18 +99,17 @@ const RIPPLE_BG_CHAR = ' '
/**
* 计算面板某一行 y 的完整波纹 cell 列表。
*
* 波纹数学v2调慢 + 扩大覆盖
* 波纹数学v3简化,移除震源高频涟漪
* dx = x - sourceX
* dy = (y - sourceY) * 1.5 y 方向视觉拉伸,行高 > 字宽)
* dist = sqrt(dx² + dy²)
* phase = dist * 0.35 - time * 0.004 (速度调慢至原 1/3
* wave = max(0, sin(phase))
* falloff = max(0, 1 - dist / 90) (覆盖半径扩到 90,让左侧 dist=65 也可见
* falloff = max(0, 1 - dist / 90) (覆盖半径扩到 90
* intensity = wave * falloff
* 震源附近 (dist < 6):叠加高频涟漪 max(intensity, 0.5 + 0.5*sin(time*0.02 - dist*1.2))
*
* 每位置强度经 intensityToColor → 颜色字符串(永不 transparent写入 cell。
* 即使 intensity=0波谷也得到最暗档 #0a0d1a 作为面板底色。
* 即使 intensity=0波谷也得到最暗档 #1a1f3a 作为面板底色。
*
* @returns 长度严格等于 width 的 Cell 数组
*/
@@ -135,13 +135,7 @@ export function computeRippleCells(args: {
// 距离衰减(覆盖半径扩到 90原 40
const falloff = Math.max(0, 1 - dist / 90)
let intensity = wave * falloff
// 震源附近高频涟漪
if (dist < 6) {
const ripple = 0.5 + 0.5 * Math.sin(time * 0.02 - dist * 1.2)
if (ripple > intensity) intensity = ripple
}
const intensity = wave * falloff
cells[x] = {
char: RIPPLE_BG_CHAR,