-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathFooter.tsx
More file actions
140 lines (127 loc) · 4.53 KB
/
Footer.tsx
File metadata and controls
140 lines (127 loc) · 4.53 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
* @license
* Copyright 2025 Qwen
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { Box, Text } from 'ink';
import { theme } from '../semantic-colors.js';
import { ContextUsageDisplay } from './ContextUsageDisplay.js';
import { useTerminalSize } from '../hooks/useTerminalSize.js';
import { AutoAcceptIndicator } from './AutoAcceptIndicator.js';
import { ShellModeIndicator } from './ShellModeIndicator.js';
import { isNarrowWidth } from '../utils/isNarrowWidth.js';
import { useStatusLine } from '../hooks/useStatusLine.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useConfig } from '../contexts/ConfigContext.js';
import { useVimMode } from '../contexts/VimModeContext.js';
import { ApprovalMode } from '@qwen-code/qwen-code-core';
import { t } from '../../i18n/index.js';
export const Footer: React.FC = () => {
const uiState = useUIState();
const config = useConfig();
const { vimEnabled, vimMode } = useVimMode();
const { text: statusLineText, padding: statusLinePadding } = useStatusLine();
const { promptTokenCount, showAutoAcceptIndicator } = {
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
showAutoAcceptIndicator: uiState.showAutoAcceptIndicator,
};
const { columns: terminalWidth } = useTerminalSize();
const isNarrow = isNarrowWidth(terminalWidth);
// Determine sandbox info from environment
const sandboxEnv = process.env['SANDBOX'];
const sandboxInfo = sandboxEnv
? sandboxEnv === 'sandbox-exec'
? 'seatbelt'
: sandboxEnv.startsWith('qwen-code')
? 'docker'
: sandboxEnv
: null;
// Check if debug mode is enabled
const debugMode = config.getDebugMode();
const contextWindowSize =
config.getContentGeneratorConfig()?.contextWindowSize;
// Left section should show exactly ONE thing at any time, in priority order.
const leftContent = uiState.ctrlCPressedOnce ? (
<Text color={theme.status.warning}>{t('Press Ctrl+C again to exit.')}</Text>
) : uiState.ctrlDPressedOnce ? (
<Text color={theme.status.warning}>{t('Press Ctrl+D again to exit.')}</Text>
) : uiState.showEscapePrompt ? (
<Text color={theme.text.secondary}>{t('Press Esc again to clear.')}</Text>
) : vimEnabled && vimMode === 'INSERT' ? (
<Text color={theme.text.secondary}>-- INSERT --</Text>
) : uiState.shellModeActive ? (
<ShellModeIndicator />
) : showAutoAcceptIndicator !== undefined &&
showAutoAcceptIndicator !== ApprovalMode.DEFAULT ? (
<AutoAcceptIndicator approvalMode={showAutoAcceptIndicator} />
) : statusLineText ? null : (
<Text color={theme.text.secondary}>{t('? for shortcuts')}</Text>
);
const rightItems: Array<{ key: string; node: React.ReactNode }> = [];
if (sandboxInfo) {
rightItems.push({
key: 'sandbox',
node: <Text color={theme.status.success}>🔒 {sandboxInfo}</Text>,
});
}
if (debugMode) {
rightItems.push({
key: 'debug',
node: <Text color={theme.status.warning}>Debug Mode</Text>,
});
}
if (promptTokenCount > 0 && contextWindowSize) {
rightItems.push({
key: 'context',
node: (
<Text color={theme.text.accent}>
<ContextUsageDisplay
promptTokenCount={promptTokenCount}
terminalWidth={terminalWidth}
contextWindowSize={contextWindowSize}
/>
</Text>
),
});
}
// When a custom status line is configured, render it as a dedicated row
// beneath the standard footer (matching upstream placement).
return (
<Box flexDirection="column" width="100%">
<Box
justifyContent="space-between"
width="100%"
flexDirection="row"
alignItems="center"
>
{/* Left Section */}
<Box
marginLeft={2}
justifyContent="flex-start"
flexDirection={isNarrow ? 'column' : 'row'}
alignItems={isNarrow ? 'flex-start' : 'center'}
>
{leftContent}
</Box>
{/* Right Section */}
<Box alignItems="center" justifyContent="flex-end" marginRight={2}>
{rightItems.map(({ key, node }, index) => (
<Box key={key} alignItems="center">
{index > 0 && <Text color={theme.text.secondary}> | </Text>}
{node}
</Box>
))}
</Box>
</Box>
{/* Custom status line row */}
{statusLineText && (
<Box paddingX={statusLinePadding}>
<Text dimColor wrap="truncate">
{statusLineText}
</Text>
</Box>
)}
</Box>
);
};