fix(mini): preserve taskbar and restore login
This commit is contained in:
@@ -9,5 +9,5 @@ npm run desktop:dev
|
||||
```
|
||||
|
||||
- Web 开发端口:`5180`
|
||||
- 桌面窗口默认宽度:`420`;启动后高度自动适配当前屏幕全高,并停靠在屏幕右侧
|
||||
- 桌面窗口默认宽度:`420`;启动后高度自动适配当前屏幕可用工作区,不遮挡 Windows 任务栏,并停靠在屏幕右侧
|
||||
- 左右停靠状态保存在本地;在 Tauri 桌面环境中会移动实际窗口。
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user