claude-code-best/packages/pokemon/src/ui/SpeciesPicker.tsx
claude-code-best 1217c453c4 feat: 同步 pkmn Dex 全部 1025 只精灵,新增 SpeciesPicker 搜索选择器
- SpeciesId 从 10 项联合类型改为 string,动态从 @pkmn/sim Dex 加载 1025 只精灵
- getSpecies() 改用 Dex.species.get() 直接查找(gen wrapper 仅覆盖 733/1025)
- SUPPLEMENT/DEX_TO_SPECIES 动态生成,未收录 species 使用默认值兜底
- names/fallback 改为 partial records,缺失时回退到 Dex 英文名/通用 sprite
- 新增 SpeciesPicker 组件(基于 FuzzyPicker),支持中英文/编号搜索选择精灵
- BattleFlow configSelect 阶段替换为 SpeciesPicker,删除旧的上下翻页逻辑
- evolution 移除 ALL_SPECIES_IDS 限制,所有 Dex 物种均可进化

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-22 15:15:19 +08:00

80 lines
2.3 KiB
TypeScript

import React, { useState, useMemo, useCallback } from 'react'
import { Box, Text, FuzzyPicker } from '@anthropic/ink'
import type { SpeciesId } from '../types'
import { ALL_SPECIES_IDS } from '../types'
import { getSpeciesData } from '../dex/species'
/** Pre-computed species entry for picker */
type SpeciesEntry = {
id: SpeciesId
name: string
displayName: string // zh name or English name
dexNumber: number
types: string[]
}
// Build all entries once (species data is cached internally by getSpeciesData)
const ALL_ENTRIES: SpeciesEntry[] = ALL_SPECIES_IDS.map(id => {
const data = getSpeciesData(id)
return {
id,
name: data.name,
displayName: (data.names as Record<string, string>).zh ?? data.name,
dexNumber: data.dexNumber,
types: data.types as string[],
}
})
/** Searchable species picker using FuzzyPicker */
export function SpeciesPicker({
onSelect,
onCancel,
title = '选择精灵',
}: {
onSelect: (speciesId: SpeciesId) => void
onCancel: () => void
title?: string
}) {
const [filtered, setFiltered] = useState<SpeciesEntry[]>(ALL_ENTRIES.slice(0, 50))
const handleQueryChange = useCallback((q: string) => {
if (!q.trim()) {
setFiltered(ALL_ENTRIES.slice(0, 50))
return
}
const lower = q.toLowerCase()
const matched = ALL_ENTRIES.filter(e =>
e.id.includes(lower) ||
e.name.toLowerCase().includes(lower) ||
e.displayName.includes(q) ||
String(e.dexNumber).includes(q)
)
setFiltered(matched.slice(0, 100))
}, [])
return (
<FuzzyPicker<SpeciesEntry>
title={title}
placeholder="输入名称或编号搜索…"
items={filtered}
getKey={item => item.id}
renderItem={(item, focused) => (
<Box>
<Text color={focused ? 'claude' : undefined} bold={focused}>
#{String(item.dexNumber).padStart(3, '0')} {item.displayName}
</Text>
{item.displayName !== item.name && (
<Text dimColor> {item.name}</Text>
)}
<Text color="inactive"> {item.types.join('/')}</Text>
</Box>
)}
onQueryChange={handleQueryChange}
onSelect={item => onSelect(item.id)}
onCancel={onCancel}
emptyMessage={q => `没有找到 "${q}" 相关的精灵`}
matchLabel={filtered.length < ALL_ENTRIES.length ? `${filtered.length} 个结果` : undefined}
/>
)
}