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
30 changes: 29 additions & 1 deletion src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,15 @@ pub fn get_domain_usage_stats(
// ============================================================================

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(rename_all = "camelCase", default)]
pub struct AppSettings {
pub theme: String,
pub auto_start: bool,
pub close_action: String, // "minimize" | "ask" | "quit"
pub polling_interval: u64, // seconds
pub idle_threshold: u64, // seconds
pub track_urls: bool,
pub hide_dock: bool,
}

impl Default for AppSettings {
Expand All @@ -328,6 +329,7 @@ impl Default for AppSettings {
polling_interval: 3,
idle_threshold: 300,
track_urls: false,
hide_dock: false,
}
}
}
Expand Down Expand Up @@ -556,6 +558,32 @@ pub fn set_autostart(app: tauri::AppHandle, enabled: bool) -> Result<(), String>
}
}

// ============================================================================
// Dock Visibility Commands
// ============================================================================

/// Set Dock icon visibility (macOS only)
#[tauri::command]
pub fn set_dock_visible(app_handle: tauri::AppHandle, visible: bool) -> Result<(), String> {
#[cfg(target_os = "macos")]
{
use tauri::ActivationPolicy;
let policy = if visible {
ActivationPolicy::Regular
} else {
ActivationPolicy::Accessory
};
app_handle
.set_activation_policy(policy)
.map_err(|e| e.to_string())?;
}
#[cfg(not(target_os = "macos"))]
{
let _ = (app_handle, visible);
}
Ok(())
}

// ============================================================================
// System Commands
// ============================================================================
Expand Down
16 changes: 16 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ pub fn run() {
tracker,
});

// Restore Dock visibility from saved settings
#[cfg(target_os = "macos")]
{
let settings_db = Database::new(db_path.to_str().unwrap())
.expect("Failed to open database for settings");
if let Ok(settings) = settings_db.get_settings() {
if settings.hide_dock {
use tauri::ActivationPolicy;
let _ = app.handle().set_activation_policy(ActivationPolicy::Accessory);
log::info!("Dock icon hidden (restored from settings)");
}
}
}

log::info!("Timlyzer initialized successfully");

Ok(())
Expand Down Expand Up @@ -110,6 +124,8 @@ pub fn run() {
commands::export_to_csv,
commands::export_to_json,
commands::update_tray_menu,
// Dock commands
commands::set_dock_visible,
// Autostart commands
commands::get_autostart,
commands::set_autostart,
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/en-US/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"language": "Language",
"autostart": "Start on boot",
"autostartDesc": "Automatically start Timlyzer when you log in",
"hideDock": "Hide Dock icon",
"hideDockDesc": "Hide the app from Dock and only show in the system tray",
"closeAction": "When closing window",
"minimize": "Minimize to tray",
"ask": "Ask every time",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/locales/zh-CN/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"language": "语言",
"autostart": "开机自启动",
"autostartDesc": "登录时自动启动 Timlyzer",
"hideDock": "隐藏 Dock 图标",
"hideDockDesc": "从 Dock 中隐藏应用,仅在系统托盘中显示",
"closeAction": "当关闭窗口时",
"minimize": "最小化到托盘",
"ask": "每次询问",
Expand Down
37 changes: 37 additions & 0 deletions src/pages/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
exportApi,
trackItemApi,
autostartApi,
dockApi,
type TrackedApp,
type DatabaseInfo,
type AppSettings,
Expand All @@ -49,6 +50,7 @@ export function SettingsPage() {
const [isSaving, setIsSaving] = useState(false);
const [exportMessage, setExportMessage] = useState("");
const [autostartEnabled, setAutostartEnabled] = useState(false);
const [isMacOS] = useState(() => navigator.userAgent.includes("Macintosh"));

// Load settings store for theme
const { theme, setTheme } = useSettingsStore();
Expand Down Expand Up @@ -288,6 +290,41 @@ export function SettingsPage() {
</button>
</label>

{/* Hide Dock Icon (macOS only) */}
{isMacOS && (
<label className="flex items-center justify-between cursor-pointer">
<div>
<div className="font-medium">{t("appearance.hideDock")}</div>
<div className="text-sm text-slate-500">
{t("appearance.hideDockDesc")}
</div>
</div>
<button
onClick={async () => {
if (!settings) return;
const newValue = !settings.hideDock;
await dockApi.setVisible(!newValue);
const updated = { ...settings, hideDock: newValue };
setSettings(updated);
await settingsApi.saveSettings(updated);
}}
className={cn(
"relative w-11 h-6 rounded-full transition-colors",
settings?.hideDock
? "bg-primary-500"
: "bg-slate-200 dark:bg-slate-700"
)}
>
<div
className={cn(
"absolute top-1 w-4 h-4 rounded-full bg-white transition-transform",
settings?.hideDock ? "translate-x-6" : "translate-x-1"
)}
/>
</button>
</label>
)}

{/* Close Action */}
<div>
<label className="text-sm font-medium mb-2 block">
Expand Down
19 changes: 19 additions & 0 deletions src/services/tauri-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export interface AppSettings {
pollingInterval: number;
idleThreshold: number;
trackUrls: boolean;
hideDock: boolean;
}

export interface TrackedApp {
Expand Down Expand Up @@ -327,6 +328,7 @@ export const settingsApi = {
pollingInterval: 3,
idleThreshold: 300,
trackUrls: false,
hideDock: false,
};
}
},
Expand Down Expand Up @@ -458,6 +460,23 @@ export const autostartApi = {
},
};

// ============================================================================
// Dock API
// ============================================================================

export const dockApi = {
/**
* Set Dock icon visibility (macOS only)
*/
setVisible: async (visible: boolean): Promise<void> => {
try {
await invoke("set_dock_visible", { visible });
} catch (error) {
console.error("setDockVisible error:", error);
}
},
};

// ============================================================================
// App control API
// ============================================================================
Expand Down