From bea29d3b738f8c11ebca2ac7058d7523534f3a3d Mon Sep 17 00:00:00 2001 From: Aska Date: Wed, 12 Aug 2026 16:41:34 +0800 Subject: [PATCH] style(frontend): redesign with Miuix HyperOS design language - Color system: Miuix dark tokens (primary #277AF7, bg #242424, surface #000) - Components: rounded-full buttons, squircle cards (18px), glass header - Messages: blue user bubbles, dark AI bubbles with typing animation - Admin dashboard: stat cards with real-time service status - Login: centered logo with gradient shadow, HyperOS input fields - Smooth transitions and scale-on-press interactions --- .../web/src/components/chat/ChatContainer.tsx | 38 +++++--- .../web/src/components/chat/ChatInput.tsx | 40 ++++---- .../web/src/components/chat/MessageList.tsx | 58 ++++++++--- .../web/src/components/layout/AppLayout.tsx | 45 +++++++-- frontend/web/src/components/layout/Header.tsx | 14 --- .../web/src/components/layout/Sidebar.tsx | 14 ++- frontend/web/src/index.css | 97 ++++++++++++++++++- frontend/web/src/pages/LoginPage.tsx | 55 ++++++++--- .../web/src/pages/admin/DashboardPage.tsx | 50 +++++++--- 9 files changed, 312 insertions(+), 99 deletions(-) delete mode 100644 frontend/web/src/components/layout/Header.tsx diff --git a/frontend/web/src/components/chat/ChatContainer.tsx b/frontend/web/src/components/chat/ChatContainer.tsx index fe465ed..004bbf9 100644 --- a/frontend/web/src/components/chat/ChatContainer.tsx +++ b/frontend/web/src/components/chat/ChatContainer.tsx @@ -23,8 +23,11 @@ export default function ChatContainer() { addMessage(userMsg) setStreaming(true) - // Add placeholder for assistant response - addMessage({ id: crypto.randomUUID(), session_id: currentSessionId, role: 'assistant', content: '', created_at: new Date().toISOString() }) + // Placeholder for assistant + addMessage({ + id: crypto.randomUUID(), session_id: currentSessionId, + role: 'assistant' as const, content: '', created_at: new Date().toISOString(), + }) send({ type: 'message', content: inputText, session_id: currentSessionId, timestamp: Date.now() }) setInput('') @@ -32,16 +35,29 @@ export default function ChatContainer() { return (
-
- -
+ {/* Chat area */} +
+
+ {messages.length === 0 ? ( +
+
+ C +
+

开始新对话吧~

+
+ ) : ( + + )} +
+
+
+ + {/* Input bar */} +
+
+ +
-
) } diff --git a/frontend/web/src/components/chat/ChatInput.tsx b/frontend/web/src/components/chat/ChatInput.tsx index 7bcdcce..d0d2737 100644 --- a/frontend/web/src/components/chat/ChatInput.tsx +++ b/frontend/web/src/components/chat/ChatInput.tsx @@ -9,30 +9,30 @@ export default function ChatInput({ value, onChange, onSend, disabled }: ChatInp const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() - onSend() + if (!disabled && value.trim()) onSend() } } return ( -
-
- onChange(e.target.value)} - onKeyDown={handleKeyDown} - disabled={disabled} - placeholder={disabled ? '思考中...' : '输入消息...'} - className="flex-1 bg-gray-800 rounded-lg px-4 py-2.5 text-sm outline-none focus:ring-2 focus:ring-purple-500 disabled:opacity-50" - /> - -
+
+ onChange(e.target.value)} + onKeyDown={handleKeyDown} + disabled={disabled} + placeholder="输入消息..." + className="flex-1 bg-transparent px-3 py-1.5 text-sm text-[#F2F2F2] placeholder:text-white/30 outline-none disabled:opacity-40" + /> +
) } diff --git a/frontend/web/src/components/chat/MessageList.tsx b/frontend/web/src/components/chat/MessageList.tsx index cc79a66..328bf2b 100644 --- a/frontend/web/src/components/chat/MessageList.tsx +++ b/frontend/web/src/components/chat/MessageList.tsx @@ -1,16 +1,8 @@ import type { Message } from '../../types/chat' export default function MessageList({ messages }: { messages: Message[] }) { - if (messages.length === 0) { - return ( -
- 开始新对话吧~ -
- ) - } - return ( -
+
{messages.map((msg) => ( ))} @@ -21,22 +13,56 @@ export default function MessageList({ messages }: { messages: Message[] }) { function MessageBubble({ message }: { message: Message }) { const isUser = message.role === 'user' const isAction = message.role === 'action' + const isSystem = message.role === 'system' + const isEmpty = message.role === 'assistant' && !message.content if (isAction) { - return
{message.content}
+ return ( +
+ + {message.content} + +
+ ) + } + + if (isSystem) { + return ( +
+ {message.content} +
+ ) } return (
-
+ C +
+ )} + +
- {message.content || (message.role === 'assistant' ? ... : null)} + {isEmpty ? ( + + + + + + ) : ( + message.content + )}
+ + {/* Spacer for user messages */} + {isUser &&
}
) } diff --git a/frontend/web/src/components/layout/AppLayout.tsx b/frontend/web/src/components/layout/AppLayout.tsx index adc288e..9952b8a 100644 --- a/frontend/web/src/components/layout/AppLayout.tsx +++ b/frontend/web/src/components/layout/AppLayout.tsx @@ -1,18 +1,47 @@ import { ReactNode } from 'react' +import { Link, useLocation } from 'react-router-dom' import { useAuthStore } from '../../store/authStore' -import Header from './Header' -import Sidebar from './Sidebar' export default function AppLayout({ children }: { children: ReactNode }) { const { username, logout } = useAuthStore() + const location = useLocation() return ( -
-
-
- -
{children}
-
+
+ {/* Header */} +
+ +
+ C +
+ Cyrene + + + +
+ + {/* Content */} +
{children}
) } diff --git a/frontend/web/src/components/layout/Header.tsx b/frontend/web/src/components/layout/Header.tsx deleted file mode 100644 index 20cd943..0000000 --- a/frontend/web/src/components/layout/Header.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Link, useLocation } from 'react-router-dom' - -export default function Header({ username, onLogout }: { username: string; onLogout: () => void }) { - return ( -
- Cyrene -
- 管理 - {username} - -
-
- ) -} diff --git a/frontend/web/src/components/layout/Sidebar.tsx b/frontend/web/src/components/layout/Sidebar.tsx index 25d79ba..1310615 100644 --- a/frontend/web/src/components/layout/Sidebar.tsx +++ b/frontend/web/src/components/layout/Sidebar.tsx @@ -16,18 +16,22 @@ export default function Sidebar() { } return ( -