mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
feat: 新增 /pokemon-battle 独立战斗命令,从 BuddyPanel 移除 Battle tab
- 新增 /pokemon-battle 命令,独立全屏战斗面板 - BattlePanel 在主 app Ink 上下文中使用 useInput,通过 inputRef 转发事件 - BuddyPanel 恢复为 Buddy/Pokédex/Egg 三 tab - BattleFlow 移除内部 useInput,改为暴露 handleInput 方法 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,11 @@ const buddy = feature('BUDDY')
|
|||||||
require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js')
|
require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js')
|
||||||
).default
|
).default
|
||||||
: null
|
: null
|
||||||
|
const pokemonBattle = feature('BUDDY')
|
||||||
|
? (
|
||||||
|
require('./commands/pokemon-battle/index.js') as typeof import('./commands/pokemon-battle/index.js')
|
||||||
|
).default
|
||||||
|
: null
|
||||||
const poor = feature('POOR')
|
const poor = feature('POOR')
|
||||||
? (
|
? (
|
||||||
require('./commands/poor/index.js') as typeof import('./commands/poor/index.js')
|
require('./commands/poor/index.js') as typeof import('./commands/poor/index.js')
|
||||||
@@ -364,6 +369,7 @@ const COMMANDS = memoize((): Command[] => [
|
|||||||
...(webCmd ? [webCmd] : []),
|
...(webCmd ? [webCmd] : []),
|
||||||
...(forkCmd ? [forkCmd] : []),
|
...(forkCmd ? [forkCmd] : []),
|
||||||
...(buddy ? [buddy] : []),
|
...(buddy ? [buddy] : []),
|
||||||
|
...(pokemonBattle ? [pokemonBattle] : []),
|
||||||
...(poor ? [poor] : []),
|
...(poor ? [poor] : []),
|
||||||
...(proactive ? [proactive] : []),
|
...(proactive ? [proactive] : []),
|
||||||
...(monitorCmd ? [monitorCmd] : []),
|
...(monitorCmd ? [monitorCmd] : []),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useState, useRef } from 'react';
|
import { useState } from 'react';
|
||||||
import { Box, Text, Pane, Tab, Tabs, useInput, type Color } from '@anthropic/ink';
|
import { Box, Text, Pane, Tab, Tabs, useInput, type Color } from '@anthropic/ink';
|
||||||
import { useSetAppState } from '../../state/AppState.js';
|
import { useSetAppState } from '../../state/AppState.js';
|
||||||
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
||||||
@@ -21,8 +21,6 @@ import { getXpProgress } from '@claude-code-best/pokemon';
|
|||||||
|
|
||||||
import { getGenderSymbol } from '@claude-code-best/pokemon';
|
import { getGenderSymbol } from '@claude-code-best/pokemon';
|
||||||
import { StatBar, SpriteAnimator, getFallbackSprite, loadSprite } from '@claude-code-best/pokemon';
|
import { StatBar, SpriteAnimator, getFallbackSprite, loadSprite } from '@claude-code-best/pokemon';
|
||||||
import { BattleFlow, loadBuddyData } from '@claude-code-best/pokemon';
|
|
||||||
import type { BattleFlowHandle } from '@claude-code-best/pokemon';
|
|
||||||
import type { LocalJSXCommandOnDone } from '../../types/command.js';
|
import type { LocalJSXCommandOnDone } from '../../types/command.js';
|
||||||
|
|
||||||
const CYAN: Color = 'ansi:cyan';
|
const CYAN: Color = 'ansi:cyan';
|
||||||
@@ -93,13 +91,6 @@ export function BuddyPanel({ buddyData, spriteLines, onClose }: BuddyPanelProps)
|
|||||||
onClose={() => onClose('buddy panel closed')}
|
onClose={() => onClose('buddy panel closed')}
|
||||||
/>
|
/>
|
||||||
</Tab>,
|
</Tab>,
|
||||||
<Tab key="battle" title="Battle">
|
|
||||||
<BattleTab
|
|
||||||
buddyData={data}
|
|
||||||
isActive={selectedTab === 'Battle'}
|
|
||||||
onUpdate={updateData}
|
|
||||||
/>
|
|
||||||
</Tab>,
|
|
||||||
<Tab key="egg" title="Egg">
|
<Tab key="egg" title="Egg">
|
||||||
<EggTab buddyData={data} />
|
<EggTab buddyData={data} />
|
||||||
</Tab>,
|
</Tab>,
|
||||||
@@ -622,43 +613,6 @@ function DexTab({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Battle Tab ──────────────────────────────────────────
|
|
||||||
|
|
||||||
function BattleTab({
|
|
||||||
buddyData,
|
|
||||||
isActive,
|
|
||||||
onUpdate,
|
|
||||||
}: {
|
|
||||||
buddyData: BuddyData;
|
|
||||||
isActive: boolean;
|
|
||||||
onUpdate: (data: BuddyData) => void;
|
|
||||||
}) {
|
|
||||||
const [battleKey, setBattleKey] = useState(0);
|
|
||||||
const inputRef = useRef<BattleFlowHandle | null>(null);
|
|
||||||
|
|
||||||
// Handle input here (in main app's Ink context) and forward to BattleFlow via ref
|
|
||||||
useInput((input, key) => {
|
|
||||||
if (!isActive) return;
|
|
||||||
inputRef.current?.handleInput(input, key);
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleClose = async () => {
|
|
||||||
const updated = await loadBuddyData();
|
|
||||||
onUpdate(updated);
|
|
||||||
setBattleKey(k => k + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BattleFlow
|
|
||||||
key={battleKey}
|
|
||||||
buddyData={buddyData}
|
|
||||||
onClose={handleClose}
|
|
||||||
isActive={isActive}
|
|
||||||
inputRef={inputRef}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Egg Tab ──────────────────────────────────────────
|
// ─── Egg Tab ──────────────────────────────────────────
|
||||||
|
|
||||||
function EggTab({ buddyData }: { buddyData: BuddyData }) {
|
function EggTab({ buddyData }: { buddyData: BuddyData }) {
|
||||||
|
|||||||
15
src/commands/pokemon-battle/index.ts
Normal file
15
src/commands/pokemon-battle/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import type { Command } from '../../commands.js'
|
||||||
|
import { isBuddyLive } from '../../buddy/useBuddyNotification.js'
|
||||||
|
|
||||||
|
const pokemonBattle = {
|
||||||
|
type: 'local-jsx',
|
||||||
|
name: 'pokemon-battle',
|
||||||
|
description: 'Start a Pokémon battle',
|
||||||
|
immediate: true,
|
||||||
|
get isHidden() {
|
||||||
|
return !isBuddyLive()
|
||||||
|
},
|
||||||
|
load: () => import('./pokemon-battle.js'),
|
||||||
|
} satisfies Command
|
||||||
|
|
||||||
|
export default pokemonBattle
|
||||||
71
src/commands/pokemon-battle/pokemon-battle.ts
Normal file
71
src/commands/pokemon-battle/pokemon-battle.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import React, { useState, useRef } from 'react'
|
||||||
|
import { useInput } from '@anthropic/ink'
|
||||||
|
import {
|
||||||
|
loadBuddyData,
|
||||||
|
saveBuddyData,
|
||||||
|
getActiveCreature,
|
||||||
|
BattleFlow,
|
||||||
|
type BuddyData,
|
||||||
|
type BattleFlowHandle,
|
||||||
|
} from '@claude-code-best/pokemon'
|
||||||
|
import type { ToolUseContext } from '../../Tool.js'
|
||||||
|
import type {
|
||||||
|
LocalJSXCommandContext,
|
||||||
|
LocalJSXCommandOnDone,
|
||||||
|
} from '../../types/command.js'
|
||||||
|
|
||||||
|
async function getOrInitBuddyData(): Promise<BuddyData> {
|
||||||
|
let data = await loadBuddyData()
|
||||||
|
if (!getActiveCreature(data)) {
|
||||||
|
data = await loadBuddyData()
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function call(
|
||||||
|
onDone: LocalJSXCommandOnDone,
|
||||||
|
_context: ToolUseContext & LocalJSXCommandContext,
|
||||||
|
_args: string,
|
||||||
|
): Promise<React.ReactNode> {
|
||||||
|
const data = await getOrInitBuddyData()
|
||||||
|
|
||||||
|
if (!getActiveCreature(data)) {
|
||||||
|
onDone('No companion yet · run /buddy first', { display: 'system' })
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return React.createElement(BattlePanel, {
|
||||||
|
buddyData: data,
|
||||||
|
onClose: () => {
|
||||||
|
onDone('battle closed', { display: 'system' })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function BattlePanel({
|
||||||
|
buddyData,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
buddyData: BuddyData
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
|
const [battleKey, setBattleKey] = useState(0)
|
||||||
|
const inputRef = useRef<BattleFlowHandle | null>(null)
|
||||||
|
|
||||||
|
useInput((input, key) => {
|
||||||
|
inputRef.current?.handleInput(input, key)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleClose = async () => {
|
||||||
|
const updated = await loadBuddyData()
|
||||||
|
setBattleKey(k => k + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return React.createElement(BattleFlow, {
|
||||||
|
key: battleKey,
|
||||||
|
buddyData,
|
||||||
|
onClose,
|
||||||
|
isActive: true,
|
||||||
|
inputRef,
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user