feat(ui): add light theme with system auto-switch + Cyrene avatar

- Miuix day/night dual theme CSS variables
- themeStore: light default, follows prefers-color-scheme
- Header: sun/moon toggle button
- All components migrated to CSS variables
- Cyrene avatar image replaces 'C' logo everywhere
- Favicon added
This commit is contained in:
Aska committed 2026-08-12 17:42:18 +08:00
1 parent 78c5f0f1b0
commit 5faa955bce
24 files changed
+337 -218

No files matched your search

@@ -40,10 +40,10 @@ export default function ChatContainer() {
<div className="max-w-2xl mx-auto px-4 py-6">
{messages.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full text-center space-y-2 pt-32">
<div className="w-16 h-16 rounded-[20px] bg-[#277AF7] flex items-center justify-center shadow-lg shadow-[#277AF7]/20 mb-2">
<span className="text-2xl font-bold text-white">C</span>
<div className="w-16 h-16 rounded-[20px] bg-[var(--mx-primary)] flex items-center justify-center shadow-lg shadow-[var(--mx-primary)]/20 mb-2">
<img src="/images/cyrene-avatar.png" alt="Cyrene" className="w-full h-full object-cover" />
</div>
<p className="text-white/50 text-sm">开始新对话吧~</p>
<p className="text-[var(--mx-text-dim)] text-sm">开始新对话吧~</p>
</div>
) : (
<MessageList messages={messages} />
@@ -53,7 +53,7 @@ export default function ChatContainer() {
</div>
{/* Input bar */}
<div className="shrink-0 border-t border-[#393939] bg-[#000]">
<div className="shrink-0 border-t border-[var(--mx-divider)] bg-[var(--mx-surface)]">
<div className="max-w-2xl mx-auto px-4 py-3">
<ChatInput value={inputText} onChange={setInput} onSend={handleSend} disabled={isStreaming} />
</div>
@@ -14,7 +14,7 @@ export default function ChatInput({ value, onChange, onSend, disabled }: ChatInp
}
return (
<div className="flex items-end gap-2 bg-[#000] rounded-[20px] border border-[#404040] focus-within:border-[#277AF7] transition-colors p-1.5">
<div className="flex items-end gap-2 bg-[var(--mx-surface)] rounded-[20px] border border-[var(--mx-outline)] focus-within:border-[var(--mx-primary)] transition-colors p-1.5">
<input
type="text"
value={value}
@@ -22,12 +22,12 @@ export default function ChatInput({ value, onChange, onSend, disabled }: ChatInp
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"
className="flex-1 bg-transparent px-3 py-1.5 text-sm text-[var(--mx-text)] placeholder:text-[var(--mx-text-dim)] outline-none disabled:opacity-40"
/>
<button
onClick={onSend}
disabled={disabled || !value.trim()}
className="w-9 h-9 bg-[#277AF7] hover:bg-[#3482FF] active:scale-90 disabled:bg-[#253E64] text-white rounded-full flex items-center justify-center shrink-0 transition-all duration-200"
className="w-9 h-9 bg-[var(--mx-primary)] hover:bg-[var(--mx-primary-hover)] active:scale-90 disabled:bg-[#253E64] text-white rounded-full flex items-center justify-center shrink-0 transition-all duration-200"
>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M2 8L14 2L8 14L6.5 9.5L2 8Z" fill="currentColor" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
@@ -19,7 +19,7 @@ function MessageBubble({ message }: { message: Message }) {
if (isAction) {
return (
<div className="flex justify-center py-1">
<span className="text-xs text-[#4788FF] bg-[#2B3B54]/80 px-3 py-1 rounded-full">
<span className="text-xs text-[var(--mx-tertiary-text)] bg-[var(--mx-badge-blue)]/80 px-3 py-1 rounded-full">
{message.content}
</span>
</div>
@@ -29,7 +29,7 @@ function MessageBubble({ message }: { message: Message }) {
if (isSystem) {
return (
<div className="flex justify-center py-1">
<span className="text-xs text-[#ffdad6] bg-[#2E0603]/80 px-3 py-1 rounded-full">{message.content}</span>
<span className="text-xs text-[var(--mx-error-text)] bg-[var(--mx-error-container)]/80 px-3 py-1 rounded-full">{message.content}</span>
</div>
)
}
@@ -38,17 +38,17 @@ function MessageBubble({ message }: { message: Message }) {
<div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>
{/* Avatar for AI */}
{!isUser && (
<div className="w-8 h-8 rounded-xl bg-[#277AF7] flex items-center justify-center shrink-0 mr-2 mt-1">
<span className="text-xs font-bold text-white">C</span>
<div className="w-8 h-8 rounded-xl bg-[var(--mx-primary)] flex items-center justify-center shrink-0 mr-2 mt-1">
<img src="/images/cyrene-avatar.png" alt="Cyrene" className="w-full h-full object-cover" />
</div>
)}
<div className={`max-w-[75%] px-4 py-2.5 text-sm leading-relaxed ${
isUser
? 'bg-[#277AF7] text-white rounded-[18px] rounded-br-[6px]'
? 'bg-[var(--mx-primary)] text-white rounded-[18px] rounded-br-[6px]'
: isEmpty
? 'bg-transparent text-transparent'
: 'bg-[#000] text-[#F2F2F2] rounded-[18px] rounded-bl-[6px] border border-[#404040]'
: 'bg-[var(--mx-surface)] text-[var(--mx-text)] rounded-[18px] rounded-bl-[6px] border border-[var(--mx-outline)]'
}`}>
{isEmpty ? (
<span className="flex gap-1.5 py-1">