重构组件样式系统为玻璃态设计风格并优化交互动效
Some checks failed
CI / Check / macos-latest (push) Has been cancelled
CI / Check / ubuntu-latest (push) Has been cancelled
CI / Check / windows-latest (push) Has been cancelled
CI / Test / macos-latest (push) Has been cancelled
CI / Test / ubuntu-latest (push) Has been cancelled
CI / Test / windows-latest (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / Secrets Scan (push) Has been cancelled
CI / Install Script Smoke Test (push) Has been cancelled
Some checks failed
CI / Check / macos-latest (push) Has been cancelled
CI / Check / ubuntu-latest (push) Has been cancelled
CI / Check / windows-latest (push) Has been cancelled
CI / Test / macos-latest (push) Has been cancelled
CI / Test / ubuntu-latest (push) Has been cancelled
CI / Test / windows-latest (push) Has been cancelled
CI / Clippy (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Security Audit (push) Has been cancelled
CI / Secrets Scan (push) Has been cancelled
CI / Install Script Smoke Test (push) Has been cancelled
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,46 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="OpenFang - Open Source Agent Operating System Dashboard">
|
||||
<meta name="theme-color" content="#020617">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<meta name="description" content="OpenFang — Open Source Agent Operating System. Manage, monitor, and orchestrate AI agents with a powerful dashboard.">
|
||||
<meta name="keywords" content="OpenFang, AI, Agent, Operating System, Dashboard, LLM, Automation">
|
||||
<meta name="author" content="OpenFang">
|
||||
<meta name="theme-color" content="#020617" media="(prefers-color-scheme: dark)">
|
||||
<meta name="theme-color" content="#F8F9FB" media="(prefers-color-scheme: light)">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<title>OpenFang Dashboard</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="icon" type="image/png" href="/logo.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/logo.png">
|
||||
<link rel="apple-touch-icon" href="/logo.png">
|
||||
|
||||
<!-- Preconnect to Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
||||
<!-- Typography: Inter (UI) + Geist Mono (Code) -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Geist+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Critical CSS for initial render -->
|
||||
<style>
|
||||
/* Prevent FOUC */
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
/* Initial theme before Alpine loads */
|
||||
html {
|
||||
background: #020617;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
background: #F8F9FB;
|
||||
color-scheme: light;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -360,14 +360,25 @@ impl PresenceManager {
|
||||
.clone();
|
||||
|
||||
// Check if user is already in another session
|
||||
if let Some(existing_user) = self.users.get(&connection_id) {
|
||||
if existing_user.session_id != session_id {
|
||||
// Leave the previous session first
|
||||
self.leave_session(connection_id)?;
|
||||
} else {
|
||||
// Already in this session
|
||||
return Ok(existing_user.clone());
|
||||
// Note: We need to clone the data and drop the reference before calling leave_session
|
||||
// to avoid a deadlock with DashMap
|
||||
let should_leave = {
|
||||
let user_ref = self.users.get(&connection_id);
|
||||
match user_ref {
|
||||
Some(u) => {
|
||||
if u.session_id == session_id {
|
||||
// Already in this session - return the user
|
||||
return Ok(u.clone());
|
||||
}
|
||||
true // Need to leave the old session
|
||||
}
|
||||
None => false
|
||||
}
|
||||
};
|
||||
// user_ref is now dropped, we can safely call leave_session
|
||||
|
||||
if should_leave {
|
||||
self.leave_session(connection_id)?;
|
||||
}
|
||||
|
||||
// Check max participants
|
||||
@@ -535,11 +546,11 @@ impl PresenceManager {
|
||||
for mut entry in self.users.iter_mut() {
|
||||
let user = entry.value_mut();
|
||||
let elapsed = now.signed_duration_since(user.last_activity);
|
||||
let elapsed_secs = elapsed.num_seconds() as u64;
|
||||
let elapsed_ms = elapsed.num_milliseconds() as u128;
|
||||
|
||||
let new_status = if elapsed_secs > self.config.away_timeout.as_secs() {
|
||||
let new_status = if elapsed_ms > self.config.away_timeout.as_millis() {
|
||||
PresenceStatus::Away
|
||||
} else if elapsed_secs > self.config.idle_timeout.as_secs() {
|
||||
} else if elapsed_ms > self.config.idle_timeout.as_millis() {
|
||||
PresenceStatus::Idle
|
||||
} else {
|
||||
PresenceStatus::Active
|
||||
@@ -566,8 +577,9 @@ impl PresenceManager {
|
||||
for entry in self.users.iter() {
|
||||
let user = entry.value();
|
||||
let elapsed = now.signed_duration_since(user.last_activity);
|
||||
let elapsed_ms = elapsed.num_milliseconds() as u64;
|
||||
|
||||
if elapsed.num_seconds() as u64 > self.config.cleanup_timeout.as_secs() {
|
||||
if u128::from(elapsed_ms) > self.config.cleanup_timeout.as_millis() {
|
||||
removed.push(user.connection_id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user