From 4c2dddb1015b457845c75e2ffcafd7353204002e Mon Sep 17 00:00:00 2001 From: Yipeng Wang Date: Sun, 12 Apr 2026 00:30:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E4=BE=A7=E6=A0=8F=E3=80=81?= =?UTF-8?q?=E5=BA=95=E6=A0=8F=E4=B8=8E=E7=8A=B6=E6=80=81=E6=8C=87=E7=A4=BA?= =?UTF-8?q?=E5=99=A8=E5=A4=9A=E9=A1=B9=E4=BD=93=E9=AA=8C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 侧栏:摄像头/屏幕/浏览器 Tab 与预览区对齐;直播/会话标签移入预览框内 - 底栏:压缩输入区高度;Footer 间距与折叠按钮布局;麦克风/举手按钮点击反馈 - 字幕:毛玻璃背景 - 状态:WebSocket 与 AI 状态文案更明确;AI 指示器与连接状态并排、颜色随状态变化 - 聊天:主容器居中对称,为收起按钮保留完整宽度 - 侧栏:Chakra Tooltip 与 Mode Menu 冲突,模式按钮改回 title - 国际化:tooltip、wsStatus、aiState 等中英文更新 Made-with: Cursor --- package-lock.json | 4 +- src/renderer/src/App.tsx | 6 +- .../src/components/canvas/canvas-styles.tsx | 8 +- .../components/footer/ai-state-indicator.tsx | 28 +++- .../src/components/footer/footer-styles.tsx | 26 +-- src/renderer/src/components/footer/footer.tsx | 105 +++++++----- .../src/components/sidebar/browser-panel.tsx | 11 +- .../src/components/sidebar/camera-panel.tsx | 9 +- .../src/components/sidebar/screen-panel.tsx | 9 +- .../src/components/sidebar/sidebar-styles.tsx | 15 +- .../src/components/sidebar/sidebar.tsx | 157 ++++++++++-------- src/renderer/src/locales/en/translation.json | 33 +++- src/renderer/src/locales/zh/translation.json | 33 +++- 13 files changed, 271 insertions(+), 173 deletions(-) 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 ( - - - + + + + +