fix(desktop): session persistence — refresh/login/context/empty-content 4-bug fix
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

1. App.tsx: add restoreSession() call on startup to prevent redirect
   to login page after refresh (isRestoring guard + BootstrapScreen)
2. CloneManager: call syncAgents() after loadClones() to restore
   currentAgent and conversation history on app load
3. zclaw-memory: add get_or_create_session() so frontend session UUID
   is persisted directly — kernel no longer creates mismatched IDs
4. openai.rs: assistant message content must be non-empty for
   Kimi/Qwen APIs — replace empty content with meaningful placeholders

Also includes admin-v2 ModelServices unified page (merge providers +
models + API keys into expandable row layout)
This commit is contained in:
iven
2026-03-31 13:38:59 +08:00
parent 3e5d64484e
commit 6cae768401
29 changed files with 1982 additions and 933 deletions

View File

@@ -6,13 +6,11 @@ import { useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { LoginForm, ProFormText } from '@ant-design/pro-components'
import { LockOutlined, UserOutlined, SafetyOutlined } from '@ant-design/icons'
import { message, Divider, Typography } from 'antd'
import { message } from 'antd'
import { authService } from '@/services/auth'
import { useAuthStore } from '@/stores/authStore'
import type { LoginRequest } from '@/types'
const { Title, Text } = Typography
export default function Login() {
const navigate = useNavigate()
const [searchParams] = useSearchParams()
@@ -50,51 +48,75 @@ export default function Login() {
}
return (
<div style={{ minHeight: '100vh', display: 'flex' }}>
{/* 左侧品牌区 */}
<div
style={{
flex: '1 1 0',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, #001529 0%, #003a70 50%, #001529 100%)',
position: 'relative',
overflow: 'hidden',
}}
<div className="min-h-screen flex">
{/* Left Brand Panel — hidden on mobile */}
<div className="hidden md:flex flex-1 flex-col items-center justify-center relative overflow-hidden"
style={{ background: 'linear-gradient(135deg, #0c0a09 0%, #1c1917 40%, #292524 100%)' }}
>
<Title level={1} style={{ color: '#fff', marginBottom: 8, letterSpacing: 4 }}>
ZCLAW
</Title>
<Text style={{ color: 'rgba(255,255,255,0.65)', fontSize: 16 }}>AI Agent </Text>
<Divider style={{ borderColor: 'rgba(22,119,255,0.3)', width: 100, minWidth: 100 }} />
<Text style={{ color: 'rgba(255,255,255,0.45)', fontSize: 13, maxWidth: 320, textAlign: 'center' }}>
AI API
</Text>
{/* Decorative gradient orb */}
<div
className="absolute w-[400px] h-[400px] rounded-full opacity-20 blur-3xl"
style={{ background: 'linear-gradient(135deg, #863bff, #47bfff)', top: '20%', left: '10%' }}
/>
<div
className="absolute w-[300px] h-[300px] rounded-full opacity-10 blur-3xl"
style={{ background: 'linear-gradient(135deg, #47bfff, #863bff)', bottom: '10%', right: '15%' }}
/>
{/* Brand content */}
<div className="relative z-10 text-center px-8">
<div
className="inline-flex items-center justify-center w-16 h-16 rounded-2xl mb-6"
style={{ background: 'linear-gradient(135deg, #863bff, #47bfff)' }}
>
<span className="text-white text-2xl font-bold">Z</span>
</div>
<h1 className="text-4xl font-bold text-white mb-3 tracking-tight">ZCLAW</h1>
<p className="text-white/50 text-base mb-8">AI Agent </p>
<div className="w-16 h-px mx-auto mb-8" style={{ background: 'linear-gradient(90deg, transparent, #863bff, #47bfff, transparent)' }} />
<p className="text-white/30 text-sm max-w-sm mx-auto leading-relaxed">
AI API
</p>
</div>
</div>
{/* 右侧登录表单 */}
<div
style={{
flex: '0 0 480px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 48,
}}
>
<div style={{ width: '100%', maxWidth: 360 }}>
<Title level={3} style={{ marginBottom: 4 }}></Title>
<Text type="secondary" style={{ display: 'block', marginBottom: 32 }}>
{/* Right Login Form */}
<div className="flex-1 md:flex-none md:w-[480px] flex items-center justify-center p-8 bg-white dark:bg-neutral-950">
<div className="w-full max-w-[360px]">
{/* Mobile logo (visible only on mobile) */}
<div className="md:hidden flex items-center gap-3 mb-10">
<div
className="flex items-center justify-center w-10 h-10 rounded-xl"
style={{ background: 'linear-gradient(135deg, #863bff, #47bfff)' }}
>
<span className="text-white font-bold">Z</span>
</div>
<span className="text-xl font-bold text-neutral-900 dark:text-white">ZCLAW</span>
</div>
<h2 className="text-2xl font-semibold text-neutral-900 dark:text-white mb-1">
</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mb-8">
</Text>
</p>
<LoginForm
onFinish={handleSubmit}
submitter={{
searchConfig: { submitText: '登录' },
submitButtonProps: { loading, block: true },
submitButtonProps: {
loading,
block: true,
style: {
height: 44,
borderRadius: 8,
fontWeight: 500,
fontSize: 15,
background: 'linear-gradient(135deg, #863bff, #47bfff)',
border: 'none',
},
},
}}
>
<ProFormText