-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathSlashCommandMenu.tsx
More file actions
76 lines (70 loc) · 2.66 KB
/
SlashCommandMenu.tsx
File metadata and controls
76 lines (70 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import { formatSlashCommandDescription, formatSlashCommandLabel } from "./slashCommands";
import type { SlashCommandItem } from "./slashCommands";
import React from "react";
import { Box, Text } from "ink";
type SlashCommandMenuProps = {
items: SlashCommandItem[];
activeIndex: number;
width: number;
maxVisible?: number;
};
const SlashCommandMenu = React.memo(function SlashCommandMenu({
items,
activeIndex,
maxVisible = 6,
width,
}: SlashCommandMenuProps): React.ReactElement | null {
// 计算标签列最佳宽度:包含前缀"> "或" "(2字符),不超过容器一半(扣除gap)
const labelColumnWidth = React.useMemo(() => {
if (items.length === 0) {
return 0;
}
const longestLabel = Math.max(...items.map((s) => s.label.length));
const contentWidth = longestLabel + 2; // +2 for prefix "> " or " "
const maxAllowed = Math.max(10, (width - 2) >> 1); // 容器50%宽度(减去gap),至少保留10列
return Math.min(contentWidth, maxAllowed);
}, [items, width]);
if (items.length === 0) {
return null;
}
// 计算可见窗口起始位置,确保 activeIndex 始终在可见区域内
const visibleStart = Math.min(
Math.max(0, activeIndex - Math.floor((maxVisible - 1) / 2)),
Math.max(0, items.length - maxVisible)
);
const visibleItems = items.slice(visibleStart, visibleStart + maxVisible);
return (
<Box flexDirection="column" marginBottom={1} width={width}>
{visibleStart > 0 ? (
<Box marginLeft={2}>
<Text dimColor>▲</Text>
</Box>
) : null}
{visibleItems.map((item, idx) => {
const actualIndex = visibleStart + idx;
return (
<Box key={item.label} gap={2} flexDirection="row" flexGrow={1}>
<Box width={labelColumnWidth} flexShrink={0}>
<Text color={actualIndex === activeIndex ? "#229ac3" : undefined} wrap="truncate-end">
{actualIndex === activeIndex ? "> " : " "}
<Text bold>{formatSlashCommandLabel(item)}</Text>
</Text>
</Box>
<Box flexGrow={1}>
<Text color={actualIndex === activeIndex ? "#229ac3" : undefined} wrap="truncate-end" dimColor>
{formatSlashCommandDescription(item.description)}
</Text>
</Box>
</Box>
);
})}
<Box marginLeft={2} flexDirection="column">
{visibleStart + visibleItems.length < items.length ? <Text dimColor>▼</Text> : null}
<Text dimColor>
({activeIndex + 1}/{items.length}) ↑↓ to navigate · Enter to select
</Text>
</Box>
</Box>
);
});
export default SlashCommandMenu;