mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-22 16:25:51 +00:00
feat(effort): 新增 EffortPanel 纯函数状态模块(PanelPosition + 移动/初始光标)
仅含纯函数与类型,无 React/Ink 依赖,便于单测。 - PANEL_POSITIONS:low → medium → high → xhigh → max → ultracode - moveLeft/moveRight:边界钳制(low 不再左移、ultracode 不再右移) - getInitialCursor:env override > displayed level Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
This commit is contained in:
101
src/components/EffortPanel/__tests__/effortPanelState.test.ts
Normal file
101
src/components/EffortPanel/__tests__/effortPanelState.test.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { describe, expect, test } from 'bun:test'
|
||||||
|
import {
|
||||||
|
END_POSITION,
|
||||||
|
HOME_POSITION,
|
||||||
|
PANEL_POSITIONS,
|
||||||
|
type PanelPosition,
|
||||||
|
getInitialCursor,
|
||||||
|
isUltracode,
|
||||||
|
moveLeft,
|
||||||
|
moveRight,
|
||||||
|
} from '../effortPanelState.js'
|
||||||
|
|
||||||
|
describe('effortPanelState', () => {
|
||||||
|
test('PANEL_POSITIONS 顺序为 low → ultracode', () => {
|
||||||
|
expect(PANEL_POSITIONS).toEqual([
|
||||||
|
'low',
|
||||||
|
'medium',
|
||||||
|
'high',
|
||||||
|
'xhigh',
|
||||||
|
'max',
|
||||||
|
'ultracode',
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('moveLeft 在 low 处保持 low', () => {
|
||||||
|
expect(moveLeft('low')).toBe('low')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('moveLeft 正常左移', () => {
|
||||||
|
expect(moveLeft('high')).toBe('medium')
|
||||||
|
expect(moveLeft('ultracode')).toBe('max')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('moveRight 在 ultracode 处保持 ultracode', () => {
|
||||||
|
expect(moveRight('ultracode')).toBe('ultracode')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('moveRight 正常右移', () => {
|
||||||
|
expect(moveRight('medium')).toBe('high')
|
||||||
|
expect(moveRight('max')).toBe('ultracode')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('HOME_POSITION 等于 low', () => {
|
||||||
|
expect(HOME_POSITION).toBe('low')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('END_POSITION 等于 ultracode', () => {
|
||||||
|
expect(END_POSITION).toBe('ultracode')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('isUltracode 守卫', () => {
|
||||||
|
expect(isUltracode('ultracode')).toBe(true)
|
||||||
|
expect(isUltracode('max')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('getInitialCursor:env override 为合法档位时返回 env 值', () => {
|
||||||
|
expect(
|
||||||
|
getInitialCursor({
|
||||||
|
envOverride: 'high',
|
||||||
|
appStateEffort: 'medium',
|
||||||
|
displayed: 'high',
|
||||||
|
}),
|
||||||
|
).toBe('high')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('getInitialCursor:env 为 null(unset)时用 displayed', () => {
|
||||||
|
expect(
|
||||||
|
getInitialCursor({
|
||||||
|
envOverride: null,
|
||||||
|
appStateEffort: undefined,
|
||||||
|
displayed: 'medium',
|
||||||
|
}),
|
||||||
|
).toBe('medium')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('getInitialCursor:env undefined 时用 displayed', () => {
|
||||||
|
expect(
|
||||||
|
getInitialCursor({
|
||||||
|
envOverride: undefined,
|
||||||
|
appStateEffort: 'high',
|
||||||
|
displayed: 'high',
|
||||||
|
}),
|
||||||
|
).toBe('high')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('getInitialCursor:env 是数值(ant-only)时落回 displayed', () => {
|
||||||
|
// 数值不是合法 PanelPosition,回退
|
||||||
|
expect(
|
||||||
|
getInitialCursor({
|
||||||
|
envOverride: 75,
|
||||||
|
appStateEffort: 'medium',
|
||||||
|
displayed: 'medium',
|
||||||
|
}),
|
||||||
|
).toBe('medium')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('PanelPosition 类型编译期检查(隐式)', () => {
|
||||||
|
const p: PanelPosition = 'xhigh'
|
||||||
|
expect(p).toBe('xhigh')
|
||||||
|
})
|
||||||
|
})
|
||||||
91
src/components/EffortPanel/effortPanelState.ts
Normal file
91
src/components/EffortPanel/effortPanelState.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import type { EffortValue } from '../../utils/effort.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 光标在面板上的位置。仅面板内部使用,不进入 AppState / settings / API。
|
||||||
|
* 'ultracode' 不是 EffortLevel;它在本面板里仅作视觉占位与文案引导。
|
||||||
|
*/
|
||||||
|
export type PanelPosition =
|
||||||
|
| 'low'
|
||||||
|
| 'medium'
|
||||||
|
| 'high'
|
||||||
|
| 'xhigh'
|
||||||
|
| 'max'
|
||||||
|
| 'ultracode'
|
||||||
|
|
||||||
|
export const PANEL_POSITIONS: readonly PanelPosition[] = [
|
||||||
|
'low',
|
||||||
|
'medium',
|
||||||
|
'high',
|
||||||
|
'xhigh',
|
||||||
|
'max',
|
||||||
|
'ultracode',
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export const HOME_POSITION: PanelPosition = 'low'
|
||||||
|
export const END_POSITION: PanelPosition = 'ultracode'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断一个值是否可作为面板光标位置(不含 ultracode,因 ultracode 仅由面板内部产生)。
|
||||||
|
*/
|
||||||
|
function isNonUltracodePosition(
|
||||||
|
value: unknown,
|
||||||
|
): value is Exclude<PanelPosition, 'ultracode'> {
|
||||||
|
return (
|
||||||
|
typeof value === 'string' &&
|
||||||
|
value !== 'ultracode' &&
|
||||||
|
(PANEL_POSITIONS as readonly string[]).includes(value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 把 EffortValue 归一化为面板可用的光标位置。
|
||||||
|
* - null / undefined / 数值(ant-only)/ ultracode → undefined(让上层用 displayed)
|
||||||
|
* - 合法 string 档位 → 返回该档位
|
||||||
|
*/
|
||||||
|
function normalizeToPanelPosition(
|
||||||
|
value: EffortValue | null | undefined,
|
||||||
|
): PanelPosition | undefined {
|
||||||
|
if (value === null || value === undefined) return undefined
|
||||||
|
if (typeof value === 'number') return undefined
|
||||||
|
if (isNonUltracodePosition(value)) {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveLeft(cursor: PanelPosition): PanelPosition {
|
||||||
|
const idx = PANEL_POSITIONS.indexOf(cursor)
|
||||||
|
if (idx <= 0) return PANEL_POSITIONS[0]
|
||||||
|
return PANEL_POSITIONS[idx - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function moveRight(cursor: PanelPosition): PanelPosition {
|
||||||
|
const idx = PANEL_POSITIONS.indexOf(cursor)
|
||||||
|
if (idx === -1 || idx >= PANEL_POSITIONS.length - 1) {
|
||||||
|
return PANEL_POSITIONS[PANEL_POSITIONS.length - 1]
|
||||||
|
}
|
||||||
|
return PANEL_POSITIONS[idx + 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isUltracode(cursor: PanelPosition): boolean {
|
||||||
|
return cursor === 'ultracode'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 决定面板挂载时的初始光标位置。
|
||||||
|
* 优先级:env override(若是合法档位)> displayed level
|
||||||
|
*
|
||||||
|
* @param envOverride getEffortEnvOverride() 的返回值:EffortValue | null | undefined
|
||||||
|
* @param appStateEffort AppState.effortValue
|
||||||
|
* @param displayed getDisplayedEffortLevel(model, appStateEffort) —— 必传,避免此处再依赖 model
|
||||||
|
*/
|
||||||
|
export function getInitialCursor(args: {
|
||||||
|
envOverride: EffortValue | null | undefined
|
||||||
|
appStateEffort: EffortValue | undefined
|
||||||
|
displayed: PanelPosition
|
||||||
|
}): PanelPosition {
|
||||||
|
const fromEnv = normalizeToPanelPosition(args.envOverride)
|
||||||
|
if (fromEnv !== undefined) return fromEnv
|
||||||
|
// displayed 已经是 EffortLevel(不含 ultracode),合法
|
||||||
|
return args.displayed
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user