refactor ui for qwen code hooks

This commit is contained in:
DennisYu07 2026-03-23 11:24:59 +08:00
parent 38caa0b218
commit b08154dbee
21 changed files with 972 additions and 357 deletions

View file

@ -0,0 +1,132 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { useState } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../../semantic-colors.js';
import { useKeypress } from '../../hooks/useKeypress.js';
import type { HookEventDisplayInfo } from './types.js';
import { SOURCE_DISPLAY_MAP } from './constants.js';
interface HookDetailStepProps {
hook: HookEventDisplayInfo;
onBack: () => void;
}
export function HookDetailStep({
hook,
onBack,
}: HookDetailStepProps): React.JSX.Element {
const hasConfigs = hook.configs.length > 0;
const [selectedIndex, setSelectedIndex] = useState(0);
// Handle keyboard navigation
useKeypress(
(key) => {
if (key.name === 'escape') {
onBack();
} else if (hasConfigs) {
if (key.name === 'up') {
setSelectedIndex((prev) => Math.max(0, prev - 1));
} else if (key.name === 'down') {
setSelectedIndex((prev) =>
Math.min(hook.configs.length - 1, prev + 1),
);
}
}
},
{ isActive: true },
);
return (
<Box flexDirection="column" paddingX={1}>
{/* Title */}
<Box marginBottom={1}>
<Text bold color={theme.text.primary}>
{hook.event}
</Text>
</Box>
{/* Description */}
{hook.description && (
<Box marginBottom={1}>
<Text color={theme.text.secondary}>{hook.description}</Text>
</Box>
)}
{/* Exit codes */}
{hook.exitCodes.length > 0 && (
<Box flexDirection="column" marginBottom={1}>
<Text bold color={theme.text.primary}>
Exit codes:
</Text>
{hook.exitCodes.map((ec, index) => (
<Box key={index}>
<Text color={theme.text.secondary}>
{` ${ec.code}: ${ec.description}`}
</Text>
</Box>
))}
</Box>
)}
<Box marginTop={1} />
{/* Configs or empty state */}
{hasConfigs ? (
<>
<Text bold color={theme.text.primary}>
Configured hooks:
</Text>
{hook.configs.map((config, index) => {
const isSelected = index === selectedIndex;
const sourceDisplay =
SOURCE_DISPLAY_MAP[config.source] || config.source;
return (
<Box key={index}>
<Box minWidth={2}>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
>
{isSelected ? '' : ' '}
</Text>
</Box>
<Text
color={isSelected ? theme.text.accent : theme.text.primary}
bold={isSelected}
>
{`${index + 1}. ${config.config.command}`}
</Text>
<Text color={theme.text.secondary}> · </Text>
<Text color={theme.text.secondary}>{sourceDisplay}</Text>
</Box>
);
})}
<Box marginTop={1}>
<Text color={theme.text.secondary}>Esc to go back</Text>
</Box>
</>
) : (
<>
<Box>
<Text color={theme.text.secondary}>
No hooks configured for this event.
</Text>
</Box>
<Box marginTop={1}>
<Text color={theme.text.secondary}>
To add hooks, edit settings.json directly or ask Qwen.
</Text>
</Box>
<Box marginTop={1}>
<Text color={theme.text.secondary}>Esc to go back</Text>
</Box>
</>
)}
</Box>
);
}