Files
Luna/WebInterface/src/components/chat/ChatInput.tsx
T
darman 969e4d6e37 Add React/TypeScript web interface with Chakra UI and SignalR integration
Implement web frontend using Vite + React + TypeScript with:
- Chakra UI component library for theming and styling
- SignalR bridge (lunaBridge) for real-time server communication
- Chat components (ChatPanel, ChatInput, ChatMessage)
- Layout components (AppLayout, Sidebar, StatusBar, SystemPanel)
- Custom useChat hook for conversation state management
- Dark/light theme support via ColorModeProvider
- Mock conversation data for development
2026-04-04 04:13:51 +02:00

87 lines
2.4 KiB
TypeScript

import { Box, Flex, Input } from '@chakra-ui/react';
import { useState, useEffect, useRef } from 'react';
import type { KeyboardEvent } from 'react';
interface ChatInputProps {
onSend: (message: string) => void;
isTyping?: boolean;
}
export const ChatInput = ({ onSend, isTyping }: ChatInputProps) => {
const [value, setValue] = useState('');
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (value.trim()) {
onSend(value.trim());
setValue('');
}
}
};
useEffect(() => {
inputRef.current?.focus();
}, []);
return (
<Box
px={8}
py={3}
bg="black"
borderTop="1px solid"
borderColor="whiteAlpha.100"
fontFamily="'JetBrains Mono', monospace"
fontSize="sm"
onClick={() => inputRef.current?.focus()}
cursor="text"
>
<Flex align="center" gap={0}>
<Box color="green.400" fontWeight="bold" flexShrink={0}>
{isTyping ? (
<Box as="span" color="gray.500">luna is processing...</Box>
) : (
'guest@luna:~ $'
)}
</Box>
<Box position="relative" flex="1" ml={2}>
<Input
ref={inputRef}
variant="flushed"
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
disabled={isTyping}
color="gray.200"
css={{ caretColor: 'transparent' }}
autoComplete="off"
spellCheck={false}
p={0}
h="auto"
lineHeight="1.5"
borderBottom="none"
_focus={{ borderBottom: 'none', boxShadow: 'none' }}
_placeholder={{ color: 'transparent' }}
/>
{!isTyping && isFocused && (
<Box
position="absolute"
top="50%"
transform="translateY(-50%)"
left={`${value.length}ch`}
w="8px"
h="1.1em"
bg="green.400"
css={{ animation: 'blink 1s step-end infinite' }}
pointerEvents="none"
/>
)}
</Box>
</Flex>
</Box>
);
};