diff --git a/package-lock.json b/package-lock.json index 6bd2ca1f..1cbf70c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "open-llm-vtuber", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "open-llm-vtuber", - "version": "1.2.0", + "version": "1.2.1", "hasInstallScript": true, "dependencies": { "@chakra-ui/react": "^3.2.3", diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 9fb874ec..f689112c 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -27,6 +27,7 @@ import { BrowserProvider } from "./context/browser-context"; import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; import Background from "./components/canvas/background"; import WebSocketStatus from "./components/canvas/ws-status"; +import AIStateIndicator from "./components/footer/ai-state-indicator"; import Subtitle from "./components/canvas/subtitle"; import { ModeProvider, useMode } from "./context/mode-context"; @@ -121,9 +122,10 @@ function AppContent(): JSX.Element { - + - + + = { + [AiStateEnum.IDLE]: 'gray.500', + [AiStateEnum.THINKING_SPEAKING]: '#7C5CFF', + [AiStateEnum.INTERRUPTED]: 'orange.500', + [AiStateEnum.LOADING]: 'yellow.500', + [AiStateEnum.LISTENING]: 'blue.400', + [AiStateEnum.WAITING]: 'gray.500', +}; function AIStateIndicator(): JSX.Element { const { t } = useTranslation(); const { aiState } = useAiState(); - const styles = footerStyles.aiIndicator; + const color = stateColors[aiState] || 'gray.500'; return ( - - {t(`aiState.${aiState}`)} + + {t(`aiState.${aiState}`)} ); } diff --git a/src/renderer/src/components/footer/footer-styles.tsx b/src/renderer/src/components/footer/footer-styles.tsx index 49769a18..86400ec6 100644 --- a/src/renderer/src/components/footer/footer-styles.tsx +++ b/src/renderer/src/components/footer/footer-styles.tsx @@ -21,15 +21,18 @@ export const footerStyles: { container: (isCollapsed) => ({ bg: isCollapsed ? 'transparent' : 'gray.800', borderTopRadius: isCollapsed ? 'none' : 'lg', - transform: isCollapsed ? 'translateY(calc(100% - 24px))' : 'translateY(0)', + transform: isCollapsed ? 'translateY(calc(100% - 20px))' : 'translateY(0)', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', height: '100%', position: 'relative', overflow: isCollapsed ? 'visible' : 'hidden', - pb: '4', }), toggleButton: { - height: '24px', + position: 'absolute', + top: 0, + left: 0, + width: '100%', + height: '20px', display: 'flex', alignItems: 'center', justifyContent: 'center', @@ -38,19 +41,20 @@ export const footerStyles: { _hover: { color: 'white' }, bg: 'transparent', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + zIndex: 1, }, actionButton: { borderRadius: '12px', - width: '50px', - height: '50px', - minW: '50px', + width: '44px', + height: '44px', + minW: '44px', }, input: { bg: 'gray.700', border: 'none', - height: '80px', + height: '50px', borderRadius: '12px', - fontSize: '18px', + fontSize: '16px', pl: '12', pr: '4', color: 'whiteAlpha.900', @@ -62,12 +66,12 @@ export const footerStyles: { bg: 'gray.700', }, resize: 'none', - minHeight: '80px', - maxHeight: '80px', + minHeight: '50px', + maxHeight: '50px', py: '0', display: 'flex', alignItems: 'center', - paddingTop: '28px', + paddingTop: '14px', lineHeight: '1.4', }, attachButton: { diff --git a/src/renderer/src/components/footer/footer.tsx b/src/renderer/src/components/footer/footer.tsx index 7c058614..600e9c45 100644 --- a/src/renderer/src/components/footer/footer.tsx +++ b/src/renderer/src/components/footer/footer.tsx @@ -7,9 +7,9 @@ import { IoHandRightSharp } from 'react-icons/io5'; import { FiChevronDown } from 'react-icons/fi'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; +import { Tooltip } from '@/components/ui/tooltip'; import { InputGroup } from '@/components/ui/input-group'; import { footerStyles } from './footer-styles'; -import AIStateIndicator from './ai-state-indicator'; import { useFooter } from '@/hooks/footer/use-footer'; // Type definitions @@ -38,40 +38,58 @@ interface MessageInputProps { } // Reusable components -const ToggleButton = memo(({ isCollapsed, onToggle }: ToggleButtonProps) => ( - - - -)); +const ToggleButton = memo(({ isCollapsed, onToggle }: ToggleButtonProps) => { + const { t } = useTranslation(); + return ( + + + + + + ); +}); ToggleButton.displayName = 'ToggleButton'; -const ActionButtons = memo(({ micOn, onMicToggle, onInterrupt }: ActionButtonsProps) => ( - - - {micOn ? : } - - - - - -)); +const ActionButtons = memo(({ micOn, onMicToggle, onInterrupt }: ActionButtonsProps) => { + const { t } = useTranslation(); + return ( + + + + {micOn ? : } + + + + + + + + + ); +}); ActionButtons.displayName = 'ActionButtons'; @@ -87,13 +105,15 @@ const MessageInput = memo(({ return ( - - - + + + + +