MCPcopy
hub / github.com/claude-code-best/claude-code / TextInput

Function TextInput

src/components/TextInput.tsx:39–129  ·  view source on GitHub ↗
(props: Props)

Source from the content-addressed store, hash-verified

37};
38
39export default function TextInput(props: Props): React.ReactNode {
40 const [theme] = useTheme();
41 const isTerminalFocused = useTerminalFocus();
42 // Hoisted to mount-time — this component re-renders on every keystroke.
43 const accessibilityEnabled = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY), []);
44 const settings = useSettings();
45 const reducedMotion = settings.prefersReducedMotion ?? false;
46
47 const voiceStateRaw = useVoiceState(s => s.voiceState);
48 const voiceState = feature('VOICE_MODE') ? voiceStateRaw : ('idle' as const);
49 const isVoiceRecording = voiceState === 'recording';
50
51 const audioLevelsRaw = useVoiceState(s => s.voiceAudioLevels);
52 const audioLevels = feature('VOICE_MODE') ? audioLevelsRaw : [];
53 const smoothedRef = useRef<number[]>(new Array(CURSOR_WAVEFORM_WIDTH).fill(0));
54
55 const needsAnimation = isVoiceRecording && !reducedMotion;
56 const [animRefRaw, animTimeRaw] = useAnimationFrame(needsAnimation ? 50 : null);
57 const animRef = feature('VOICE_MODE') ? animRefRaw : () => {};
58 const animTime = feature('VOICE_MODE') ? animTimeRaw : 0;
59
60 // Show hint when terminal regains focus and clipboard has an image
61 useClipboardImageHint(isTerminalFocused, !!props.onImagePaste);
62
63 // Cursor invert function: mini waveform during voice recording,
64 // standard chalk.inverse otherwise. No warmup pulse — the ~120ms
65 // warmup window is too short for a 1s-period pulse to register, and
66 // driving TextInput re-renders at 50ms during warmup (while spaces
67 // are simultaneously arriving every 30-80ms) causes visible stutter.
68 const canShowCursor = isTerminalFocused && !accessibilityEnabled;
69 let invert: (text: string) => string;
70 if (!canShowCursor) {
71 invert = (text: string) => text;
72 } else if (isVoiceRecording && !reducedMotion) {
73 // Single-bar waveform from the latest audio level
74 const smoothed = smoothedRef.current;
75 const raw = audioLevels.length > 0 ? (audioLevels[audioLevels.length - 1] ?? 0) : 0;
76 const target = Math.min(raw * LEVEL_BOOST, 1);
77 smoothed[0] = (smoothed[0] ?? 0) * SMOOTH + target * (1 - SMOOTH);
78 const displayLevel = smoothed[0] ?? 0;
79 const barIndex = Math.max(1, Math.min(Math.round(displayLevel * (BARS.length - 1)), BARS.length - 1));
80 const isSilent = raw < SILENCE_THRESHOLD;
81 const hue = ((animTime / 1000) * 90) % 360;
82 const { r, g, b } = isSilent ? { r: 128, g: 128, b: 128 } : hueToRgb(hue);
83 invert = () => chalk.rgb(r, g, b)(BARS[barIndex]!);
84 } else {
85 invert = chalk.inverse;
86 }
87
88 const textInputState = useTextInput({
89 value: props.value,
90 onChange: props.onChange,
91 onSubmit: props.onSubmit,
92 onExit: props.onExit,
93 onExitMessage: props.onExitMessage,
94 onHistoryReset: props.onHistoryReset,
95 onHistoryUp: props.onHistoryUp,
96 onHistoryDown: props.onHistoryDown,

Callers

nothing calls this directly

Calls 11

useThemeFunction · 0.90
useTerminalFocusFunction · 0.90
useAnimationFrameFunction · 0.90
colorFunction · 0.90
useSettingsFunction · 0.85
useVoiceStateFunction · 0.85
useClipboardImageHintFunction · 0.85
hueToRgbFunction · 0.85
useTextInputFunction · 0.85
maxMethod · 0.80
isEnvTruthyFunction · 0.50

Tested by

no test coverage detected