Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions BitFun-Installer/scripts/sync-theme-i18n.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const THEME_IDS = [
"bitfun-china-night",
"bitfun-cyber",
"bitfun-slate",
"bitfun-tokyo-night",
];

function readJson(filePath) {
Expand Down
1 change: 1 addition & 0 deletions BitFun-Installer/src-tauri/src/installer/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,7 @@ pub fn set_theme_preference(theme_preference: String) -> Result<(), String> {
"bitfun-china-night",
"bitfun-cyber",
"bitfun-slate",
"bitfun-tokyo-night",
];
if !allowed.contains(&theme_preference.as_str()) {
return Err("Unsupported theme preference".to_string());
Expand Down
3 changes: 2 additions & 1 deletion BitFun-Installer/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@
"bitfun-china-style": "Ink Charm",
"bitfun-china-night": "Ink Night",
"bitfun-cyber": "Cyber",
"bitfun-slate": "Slate"
"bitfun-slate": "Slate",
"bitfun-tokyo-night": "Tokyo Night"
}
},
"complete": {
Expand Down
3 changes: 2 additions & 1 deletion BitFun-Installer/src/i18n/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@
"bitfun-china-style": "墨韻",
"bitfun-china-night": "墨夜",
"bitfun-cyber": "賽博",
"bitfun-slate": "石板灰"
"bitfun-slate": "石板灰",
"bitfun-tokyo-night": "東京夜"
}
},
"complete": {
Expand Down
3 changes: 2 additions & 1 deletion BitFun-Installer/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@
"bitfun-china-style": "墨韵",
"bitfun-china-night": "墨夜",
"bitfun-cyber": "赛博",
"bitfun-slate": "石板灰"
"bitfun-slate": "石板灰",
"bitfun-tokyo-night": "东京夜"
}
},
"complete": {
Expand Down
15 changes: 15 additions & 0 deletions BitFun-Installer/src/theme/installerThemesData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,20 @@ export const THEMES: InstallerTheme[] = [
element: { subtle: 'rgba(0, 230, 255, 0.06)', soft: 'rgba(0, 230, 255, 0.09)', base: 'rgba(0, 230, 255, 0.13)', medium: 'rgba(0, 230, 255, 0.17)', strong: 'rgba(0, 230, 255, 0.22)', elevated: 'rgba(0, 230, 255, 0.27)' },
},
},
{
id: 'bitfun-tokyo-night',
name: 'Tokyo Night',
type: 'dark',
colors: {
background: { primary: '#1a1b26', secondary: '#16161e', tertiary: '#14141b', quaternary: '#1e202e', elevated: '#20222c', workbench: '#16161e', flowchat: '#1a1b26', tooltip: 'rgba(22, 22, 30, 0.94)' },
text: { primary: '#c0caf5', secondary: '#a9b1d6', muted: '#787c99', disabled: '#545c7e' },
accent: { '50': 'rgba(122, 162, 247, 0.05)', '100': 'rgba(122, 162, 247, 0.08)', '200': 'rgba(122, 162, 247, 0.15)', '300': 'rgba(122, 162, 247, 0.25)', '400': 'rgba(122, 162, 247, 0.4)', '500': '#7aa2f7', '600': '#6183bb', '700': 'rgba(97, 131, 187, 0.85)', '800': 'rgba(97, 131, 187, 0.95)' },
purple: { '50': 'rgba(187, 154, 247, 0.05)', '100': 'rgba(187, 154, 247, 0.08)', '200': 'rgba(187, 154, 247, 0.15)', '300': 'rgba(187, 154, 247, 0.25)', '400': 'rgba(187, 154, 247, 0.4)', '500': '#bb9af7', '600': '#9d7cd8', '700': 'rgba(157, 124, 216, 0.85)', '800': 'rgba(157, 124, 216, 0.95)' },
semantic: { success: '#9ece6a', warning: '#e0af68', error: '#f7768e', info: '#7dcfff', highlight: '#e0af68', highlightBg: 'rgba(224, 175, 104, 0.15)' },
border: { subtle: 'rgba(54, 59, 84, 0.45)', base: 'rgba(54, 59, 84, 0.6)', medium: 'rgba(54, 59, 84, 0.72)', strong: 'rgba(54, 59, 84, 0.85)', prominent: 'rgba(122, 162, 247, 0.45)' },
element: { subtle: 'rgba(122, 162, 247, 0.06)', soft: 'rgba(122, 162, 247, 0.08)', base: 'rgba(122, 162, 247, 0.11)', medium: 'rgba(122, 162, 247, 0.14)', strong: 'rgba(122, 162, 247, 0.18)', elevated: 'rgba(122, 162, 247, 0.22)' },
},
},
{
id: 'bitfun-slate',
name: 'Slate',
Expand All @@ -158,6 +172,7 @@ export const THEME_DISPLAY_ORDER: ThemeId[] = [
'bitfun-china-style',
'bitfun-china-night',
'bitfun-cyber',
'bitfun-tokyo-night',
];

export function findInstallerThemeById(id: ThemeId): InstallerTheme {
Expand Down
3 changes: 2 additions & 1 deletion BitFun-Installer/src/types/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export type ThemeId =
| 'bitfun-china-style'
| 'bitfun-china-night'
| 'bitfun-cyber'
| 'bitfun-slate';
| 'bitfun-slate'
| 'bitfun-tokyo-night';

/** Matches main app `themes.current` when following OS appearance. */
export const SYSTEM_THEME_ID = 'system' as const;
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ similar = "2.5"
urlencoding = "2.1"

# Tauri (desktop only)
tauri = { version = "2", features = ["unstable", "macos-private-api"] }
tauri = { version = "2", features = ["unstable", "macos-private-api", "tray-icon"] }
tauri-plugin-opener = "2"
tauri-plugin-dialog = "2.6"
tauri-plugin-fs = "2"
Expand Down
9 changes: 9 additions & 0 deletions src/apps/desktop/src/api/i18n_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ pub async fn i18n_set_language(
&_app, language, mode, edit_mode,
);
}

// Rebuild the system tray menu in the new language.
{
let app_handle = _app.clone();
tauri::async_runtime::spawn(async move {
crate::tray::rebuild_tray_menu_public(&app_handle).await;
});
}

Ok(format!("Language switched to: {}", language))
}
Err(e) => {
Expand Down
25 changes: 24 additions & 1 deletion src/apps/desktop/src/api/system_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex};
use crate::api::app_state::AppState;
use bitfun_core::service::system;
use serde::{Deserialize, Serialize};
use tauri::{AppHandle, Emitter, State};
use tauri::{AppHandle, Emitter, Manager, State};
use tauri_plugin_updater::UpdaterExt;

/// Emitted during `install_update` download; matches `installUpdateWithProgress` / frontend listener.
Expand Down Expand Up @@ -309,6 +309,29 @@ pub struct SendNotificationRequest {
pub body: Option<String>,
}

// ─── Window / Tray behavior commands ─────────────────────────────────────────

/// Immediately exit the application (used by the "ask" dialog when the user
/// chooses to quit rather than minimize to tray).
#[tauri::command]
pub async fn quit_app(app: tauri::AppHandle) -> Result<(), String> {
log::info!("Quit requested via quit_app command");
crate::perform_process_exit_cleanup();
app.exit(0);
Ok(())
}

/// Hide the main window so it lives only in the system tray (used by the "ask"
/// dialog when the user chooses to minimize instead of quitting).
#[tauri::command]
pub async fn minimize_to_tray(app: tauri::AppHandle) -> Result<(), String> {
if let Some(window) = app.get_webview_window("main") {
window.hide().map_err(|e| e.to_string())?;
log::info!("Main window minimized to tray via command");
}
Ok(())
}

/// Send an OS-level desktop notification (Windows toast / macOS notification center).
#[tauri::command]
pub async fn send_system_notification(
Expand Down
30 changes: 23 additions & 7 deletions src/apps/desktop/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod computer_use;
pub mod logging;
pub mod macos_menubar;
pub mod theme;
pub mod tray;

use bitfun_core::agentic::tools::computer_use_capability::set_computer_use_desktop_available;
use bitfun_core::agentic::tools::computer_use_host::ComputerUseHostRef;
Expand All @@ -21,7 +22,6 @@ use std::sync::{
Arc,
};
use std::time::Instant;
#[cfg(target_os = "macos")]
use tauri::Emitter;
use tauri::Manager;

Expand Down Expand Up @@ -77,12 +77,16 @@ static MAIN_WINDOW_HIDDEN_ON_MACOS: AtomicBool = AtomicBool::new(false);
#[cfg(target_os = "macos")]
static MAIN_WINDOW_CLOSE_PENDING_ON_MACOS: AtomicBool = AtomicBool::new(false);

#[cfg(target_os = "macos")]
const MAIN_WINDOW_CLOSE_REQUESTED_EVENT: &str = "bitfun_main_window_close_requested";

#[cfg(target_os = "macos")]
const MAIN_WINDOW_CLOSE_FALLBACK_HIDE_MS: u64 = 2_500;

// ─── Close-button behavior ────────────────────────────────────────────────────
// The close-button behavior is owned by the frontend; the Rust window-event
// handler only emits a notification event and the frontend decides what to do.
// No per-platform caching needed here.

#[cfg(target_os = "macos")]
pub(crate) fn mark_main_window_hidden_on_macos(hidden: bool) {
MAIN_WINDOW_HIDDEN_ON_MACOS.store(hidden, Ordering::SeqCst);
Expand Down Expand Up @@ -482,6 +486,11 @@ pub async fn run() {

logging::spawn_log_cleanup_task();

// Set up system tray icon.
if let Err(error) = crate::tray::setup_tray(app) {
log::warn!("Failed to set up system tray: {}", error);
}

log::info!("BitFun Desktop started successfully");
Ok(())
})
Expand Down Expand Up @@ -524,11 +533,16 @@ pub async fn run() {
}

#[cfg(not(target_os = "macos"))]
if let tauri::WindowEvent::CloseRequested { .. } = event {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
if window.label() == "main" {
if perform_process_exit_cleanup() {
log::info!("Main window close requested, cleaning up");
window.app_handle().exit(0);
// Prevent the OS from closing the window; let the frontend
// decide whether to minimize to tray, show a dialog, or quit.
api.prevent_close();
if let Err(error) = window.emit(MAIN_WINDOW_CLOSE_REQUESTED_EVENT, ()) {
log::warn!(
"Failed to emit main window close request event: {}",
error
);
}
}
}
Expand Down Expand Up @@ -892,6 +906,8 @@ pub async fn run() {
install_update,
restart_app,
send_system_notification,
api::system_api::quit_app,
api::system_api::minimize_to_tray,
check_command_exists,
check_commands_exist,
run_system_command,
Expand Down Expand Up @@ -1214,7 +1230,7 @@ fn setup_panic_hook() {
}));
}

fn perform_process_exit_cleanup() -> bool {
pub(crate) fn perform_process_exit_cleanup() -> bool {
static CLEANUP_DONE: AtomicBool = AtomicBool::new(false);

if CLEANUP_DONE
Expand Down
10 changes: 10 additions & 0 deletions src/apps/desktop/src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ impl ThemeConfig {
text_muted: "rgba(255, 255, 255, 0.4)".to_string(),
accent_color: "#00e6ff".to_string(),
}),
"bitfun-tokyo-night" => Some(Self {
id: theme_id.to_string(),
bg_primary: "#1a1b26".to_string(),
bg_secondary: "#16161e".to_string(),
bg_scene: "#1a1b26".to_string(),
is_light: false,
text_primary: "#c0caf5".to_string(),
text_muted: "rgba(255, 255, 255, 0.4)".to_string(),
accent_color: "#7aa2f7".to_string(),
}),
"bitfun-china-night" => Some(Self {
id: theme_id.to_string(),
bg_primary: "#1a1814".to_string(),
Expand Down
Loading
Loading