fix(mini): preserve taskbar and restore login

This commit is contained in:
2026-07-22 19:03:24 +08:00
parent aeb5276a73
commit b01928a5e4
9 changed files with 49 additions and 16 deletions

View File

@@ -9,5 +9,5 @@ npm run desktop:dev
```
- Web 开发端口:`5180`
- 桌面窗口默认宽度:`420`;启动后高度自动适配当前屏幕全高,并停靠在屏幕右侧
- 桌面窗口默认宽度:`420`;启动后高度自动适配当前屏幕可用工作区,不遮挡 Windows 任务栏,并停靠在屏幕右侧
- 左右停靠状态保存在本地;在 Tauri 桌面环境中会移动实际窗口。

View File

@@ -58,9 +58,8 @@ await page.route('http://localhost:9150/api/v1/**', async (route) => {
await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' })
await page.screenshot({ path: 'test-results/mini-login.png', fullPage: true })
await page.locator('.login-note input').nth(1).fill('demo@senlin.ai')
await page.locator('.login-note input').nth(2).fill('password123')
await page.getByRole('button', { name: '连接工作台' }).click()
const loginDefaults = await page.locator('.login-note input').evaluateAll((inputs) => inputs.map((input) => input.value))
await page.locator('.login-note input').nth(2).press('Enter')
await page.waitForSelector('.mini-shell')
await page.waitForTimeout(400)
await page.screenshot({ path: 'test-results/mini-home-right.png', fullPage: true })
@@ -97,6 +96,7 @@ await browser.close()
await server.close()
const failures = []
if (loginDefaults[1] !== 'demo@senlin.ai' || loginDefaults[2] !== 'password123') failures.push(`login defaults failed: ${JSON.stringify(loginDefaults)}`)
if (errors.length) failures.push(`console errors: ${errors.join('; ')}`)
if (home.width !== 420) failures.push(`expected 420px shell, got ${home.width}`)
if (home.navCount !== 5) failures.push(`expected five top navigation items, got ${home.navCount}`)

View File

@@ -7,20 +7,19 @@ fn dock_window(window: Window, side: String) -> Result<(), String> {
.map_err(|error| error.to_string())?
.ok_or_else(|| "无法识别当前屏幕".to_string())?;
let window_size = window.outer_size().map_err(|error| error.to_string())?;
let monitor_position = monitor.position();
let monitor_size = monitor.size();
let dock_size = PhysicalSize::new(window_size.width, monitor_size.height);
let work_area = monitor.work_area();
let dock_size = PhysicalSize::new(window_size.width, work_area.size.height);
let x = if side == "left" {
monitor_position.x
work_area.position.x
} else {
monitor_position.x + monitor_size.width as i32 - dock_size.width as i32
work_area.position.x + work_area.size.width as i32 - dock_size.width as i32
};
window
.set_size(dock_size)
.map_err(|error| error.to_string())?;
window
.set_position(PhysicalPosition::new(x, monitor_position.y))
.set_position(PhysicalPosition::new(x, work_area.position.y))
.map_err(|error| error.to_string())?;
window
.set_always_on_top(true)

View File

@@ -307,13 +307,27 @@ function MoreView({ workspace, openAction }: ViewProps) {
function Login({ onLogin }: { onLogin: (session: ApiSession) => void }) {
const [server, setServer] = useState('http://localhost:9150')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [email, setEmail] = useState('demo@senlin.ai')
const [password, setPassword] = useState('password123')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
async function submitLogin() {
if (loading) return
setLoading(true)
setError('')
try {
onLogin(await login(server, email.trim(), password))
} catch (requestError) {
setError(errorMessage(requestError))
} finally {
setLoading(false)
}
}
return (
<div className="mini-login">
<div className="login-note">
<form className="login-note" onSubmit={(event) => { event.preventDefault(); void submitLogin() }}>
<img src="/senlinai-icon.svg" alt="" />
<Title heading={3}>AI Mini</Title>
<Text type="secondary"></Text>
@@ -321,9 +335,9 @@ function Login({ onLogin }: { onLogin: (session: ApiSession) => void }) {
<label><Input value={email} onChange={setEmail} /></label>
<label><Input.Password value={password} onChange={setPassword} /></label>
{error ? <Alert type="error" content={error} /> : null}
<Button type="primary" long loading={loading} onClick={async () => { setLoading(true); setError(''); try { onLogin(await login(server, email, password)) } catch (requestError) { setError(errorMessage(requestError)) } finally { setLoading(false) } }}></Button>
<Button type="primary" htmlType="submit" long loading={loading}></Button>
<small>v1.0 </small>
</div>
</form>
</div>
)
}

View File

@@ -192,7 +192,9 @@ function useSessionBase(session: ApiSession) {
}
function normalizeBaseUrl(value: string) {
return value.trim().replace(/\/+$/, '')
const normalized = value.trim().replace(/\/+$/, '')
if (!normalized || /^https?:\/\//i.test(normalized)) return normalized
return `http://${normalized}`
}
function slugify(value: string) {