Compare commits
8 Commits
21bb3131a0
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
aef0f74616
|
|||
|
886b460d5e
|
|||
|
27e98a527f
|
|||
|
230a2836a6
|
|||
|
3514f450c9
|
|||
|
6b645dedf0
|
|||
|
3cb14be4bc
|
|||
|
be65e275b3
|
@@ -250,6 +250,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -259,6 +260,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_USER_SELECTED_FILES = readonly;
|
ENABLE_USER_SELECTED_FILES = readonly;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -274,7 +276,6 @@
|
|||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
INFOPLIST_KEY_LSUIElement = YES;
|
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -283,6 +284,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -292,6 +294,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
ENABLE_USER_SELECTED_FILES = readonly;
|
ENABLE_USER_SELECTED_FILES = readonly;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_LSUIElement = YES;
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@@ -307,7 +310,6 @@
|
|||||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
INFOPLIST_KEY_LSUIElement = YES;
|
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Foundation
|
|||||||
import Combine
|
import Combine
|
||||||
import IOKit.pwr_mgt
|
import IOKit.pwr_mgt
|
||||||
import ServiceManagement
|
import ServiceManagement
|
||||||
|
import AppKit
|
||||||
|
|
||||||
// MARK: - App State Manager
|
// MARK: - App State Manager
|
||||||
class AppState: ObservableObject {
|
class AppState: ObservableObject {
|
||||||
@@ -10,20 +11,12 @@ class AppState: ObservableObject {
|
|||||||
// 功能的总开关
|
// 功能的总开关
|
||||||
@Published var isFunctionEnabled: Bool = false
|
@Published var isFunctionEnabled: Bool = false
|
||||||
|
|
||||||
// 用户当前是否空闲
|
|
||||||
@Published var isUserIdle: Bool = false
|
|
||||||
|
|
||||||
// 自动启动设置
|
// 自动启动设置
|
||||||
@Published var isLaunchAtLogin: Bool = false
|
@Published var isLaunchAtLogin: Bool = false
|
||||||
|
|
||||||
// 内部属性
|
// 内部属性
|
||||||
private var monitorTimer: Timer?
|
|
||||||
private var caffeinateProcess: Process? // 直接使用Process对象,但简化实现
|
private var caffeinateProcess: Process? // 直接使用Process对象,但简化实现
|
||||||
|
|
||||||
// 配置参数
|
|
||||||
private let idleThreshold: TimeInterval = 300 // 5分钟无操作视为空闲
|
|
||||||
private let checkInterval: TimeInterval = 10 // 每10秒检查一次
|
|
||||||
|
|
||||||
// 初始化方法
|
// 初始化方法
|
||||||
init() {
|
init() {
|
||||||
// 检查并设置初始的自动启动状态
|
// 检查并设置初始的自动启动状态
|
||||||
@@ -36,52 +29,11 @@ class AppState: ObservableObject {
|
|||||||
func toggleFunction() {
|
func toggleFunction() {
|
||||||
isFunctionEnabled.toggle()
|
isFunctionEnabled.toggle()
|
||||||
if isFunctionEnabled {
|
if isFunctionEnabled {
|
||||||
// 开启功能,立即开始一轮检查,并设置定时器
|
// 开启功能,直接启动caffeinate,不检查用户活动
|
||||||
performCheck()
|
|
||||||
startMonitoringTimer()
|
|
||||||
} else {
|
|
||||||
// 关闭功能,停止一切
|
|
||||||
stopMonitoringTimer()
|
|
||||||
stopCaffeinate()
|
|
||||||
updateUserIdleStatus(false) // 重置状态
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Monitoring Logic
|
|
||||||
|
|
||||||
private func startMonitoringTimer() {
|
|
||||||
guard monitorTimer == nil else { return }
|
|
||||||
print("开始监控...")
|
|
||||||
// 使用 weak self 避免循环引用
|
|
||||||
monitorTimer = Timer.scheduledTimer(withTimeInterval: checkInterval, repeats: true) { [weak self] _ in
|
|
||||||
self?.performCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func stopMonitoringTimer() {
|
|
||||||
monitorTimer?.invalidate()
|
|
||||||
monitorTimer = nil
|
|
||||||
print("监控已停止。")
|
|
||||||
}
|
|
||||||
|
|
||||||
private func performCheck() {
|
|
||||||
// 1. 获取系统空闲时间
|
|
||||||
let idleTime = getSystemIdleTime()
|
|
||||||
|
|
||||||
// 2. 判断是否空闲
|
|
||||||
let isIdleNow = (idleTime >= idleThreshold)
|
|
||||||
|
|
||||||
// 3. 如果用户状态发生了变化,则采取行动
|
|
||||||
if isIdleNow != isUserIdle {
|
|
||||||
updateUserIdleStatus(isIdleNow)
|
|
||||||
|
|
||||||
if isIdleNow {
|
|
||||||
// 从“活动中”变为“空闲”
|
|
||||||
stopCaffeinate()
|
|
||||||
} else {
|
|
||||||
// 从“空闲”变为“活动中”
|
|
||||||
startCaffeinate()
|
startCaffeinate()
|
||||||
}
|
} else {
|
||||||
|
// 关闭功能,停止caffeinate
|
||||||
|
stopCaffeinate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +76,7 @@ class AppState: ObservableObject {
|
|||||||
// 简化实现,直接启动 caffeinate 进程
|
// 简化实现,直接启动 caffeinate 进程
|
||||||
caffeinateProcess = Process()
|
caffeinateProcess = Process()
|
||||||
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
|
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
|
||||||
caffeinateProcess?.arguments = ["-d"] // -d 指示系统不要关闭显示屏
|
caffeinateProcess?.arguments = ["-i", "-s", "-d"] // -i 防止用户不活动导致睡眠,-s 防止系统进入睡眠,-d 防止显示屏关闭
|
||||||
|
|
||||||
// 配置输出,避免产生不必要的输出
|
// 配置输出,避免产生不必要的输出
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
@@ -154,14 +106,7 @@ class AppState: ObservableObject {
|
|||||||
print("已重置 caffeinate 进程引用")
|
print("已重置 caffeinate 进程引用")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UI State Update
|
|
||||||
|
|
||||||
/// 在主线程更新用户空闲状态
|
|
||||||
private func updateUserIdleStatus(_ isIdle: Bool) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.isUserIdle = isIdle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Launch at Login Management
|
// MARK: - Launch at Login Management
|
||||||
|
|
||||||
@@ -191,11 +136,66 @@ class AppState: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Permission Management
|
||||||
|
|
||||||
|
/// 检查应用程序所需的权限
|
||||||
|
func checkPermissions() {
|
||||||
|
print("检查应用程序权限...")
|
||||||
|
|
||||||
|
// 检查自动启动权限状态
|
||||||
|
checkLaunchAtLoginStatus()
|
||||||
|
print("自动启动权限: \(isLaunchAtLogin ? "已启用" : "已禁用")")
|
||||||
|
|
||||||
|
// 检查是否可以执行系统命令
|
||||||
|
let canExecuteSystemCommands = checkSystemCommandPermission()
|
||||||
|
print("系统命令执行权限: \(canExecuteSystemCommands ? "已允许" : "已拒绝")")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查是否可以执行系统命令
|
||||||
|
private func checkSystemCommandPermission() -> Bool {
|
||||||
|
// 尝试运行一个简单的系统命令来检查权限
|
||||||
|
let process = Process()
|
||||||
|
process.executableURL = URL(fileURLWithPath: "/bin/echo")
|
||||||
|
process.arguments = ["test"]
|
||||||
|
|
||||||
|
do {
|
||||||
|
try process.run()
|
||||||
|
process.waitUntilExit()
|
||||||
|
return process.terminationStatus == 0
|
||||||
|
} catch {
|
||||||
|
print("执行系统命令失败: \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开系统设置的隐私与安全性页面
|
||||||
|
func openSecuritySettings() {
|
||||||
|
print("打开系统设置的隐私与安全性页面...")
|
||||||
|
|
||||||
|
let settingsUrl = URL(string: "x-apple.systempreferences:com.apple.preference.security")!
|
||||||
|
if NSWorkspace.shared.open(settingsUrl) {
|
||||||
|
print("已打开系统设置页面")
|
||||||
|
} else {
|
||||||
|
print("打开系统设置页面失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 打开系统设置的登录项页面
|
||||||
|
func openLoginItemsSettings() {
|
||||||
|
print("打开系统设置的登录项页面...")
|
||||||
|
|
||||||
|
let settingsUrl = URL(string: "x-apple.systempreferences:com.apple.preference.usersandgroups?LoginItems")!
|
||||||
|
if NSWorkspace.shared.open(settingsUrl) {
|
||||||
|
print("已打开登录项设置页面")
|
||||||
|
} else {
|
||||||
|
print("打开登录项设置页面失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Deinitializer
|
// MARK: - Deinitializer
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
// 确保 App 退出时清理资源
|
// 确保 App 退出时清理资源
|
||||||
stopMonitoringTimer()
|
|
||||||
stopCaffeinate()
|
stopCaffeinate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,61 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_16x16.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "16x16"
|
"size" : "16x16"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_16x16@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "16x16"
|
"size" : "16x16"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_32x32.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "32x32"
|
"size" : "32x32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_32x32@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "32x32"
|
"size" : "32x32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_128x128.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "128x128"
|
"size" : "128x128"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_128x128@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "128x128"
|
"size" : "128x128"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_256x256.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "256x256"
|
"size" : "256x256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_256x256@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "256x256"
|
"size" : "256x256"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_512x512.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "1x",
|
"scale" : "1x",
|
||||||
"size" : "512x512"
|
"size" : "512x512"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "icon_512x512@2x.png",
|
||||||
"idiom" : "mac",
|
"idiom" : "mac",
|
||||||
"scale" : "2x",
|
"scale" : "2x",
|
||||||
"size" : "512x512"
|
"size" : "512x512"
|
||||||
|
|||||||
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
PowerBar/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
@@ -55,12 +55,15 @@ struct MenuContent: View {
|
|||||||
// 主要操作按钮:切换功能
|
// 主要操作按钮:切换功能
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// 点击按钮,切换功能的开关状态
|
// 点击按钮,切换功能的开关状态
|
||||||
|
withAnimation(.easeInOut(duration: 0.3)) {
|
||||||
appState.toggleFunction()
|
appState.toggleFunction()
|
||||||
|
}
|
||||||
}) {
|
}) {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Image(systemName: appState.isFunctionEnabled ? "stop.circle.fill" : "play.circle.fill")
|
Image(systemName: appState.isFunctionEnabled ? "stop.circle.fill" : "play.circle.fill")
|
||||||
.foregroundColor(appState.isFunctionEnabled ? .red : .green)
|
.foregroundColor(appState.isFunctionEnabled ? .red : .green)
|
||||||
.font(.title2)
|
.font(.title2)
|
||||||
|
.transition(.scale(scale: 0.8, anchor: .center))
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
Text(appState.isFunctionEnabled ? "关闭常亮" : "开启常亮")
|
Text(appState.isFunctionEnabled ? "关闭常亮" : "开启常亮")
|
||||||
.font(.body)
|
.font(.body)
|
||||||
@@ -74,6 +77,7 @@ struct MenuContent: View {
|
|||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(appState.isFunctionEnabled ? Color.red.opacity(0.1) : Color.green.opacity(0.1))
|
.fill(appState.isFunctionEnabled ? Color.red.opacity(0.1) : Color.green.opacity(0.1))
|
||||||
|
.transition(.opacity)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
@@ -89,17 +93,10 @@ struct MenuContent: View {
|
|||||||
if appState.isFunctionEnabled {
|
if appState.isFunctionEnabled {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
Circle()
|
Circle()
|
||||||
.fill(appState.isUserIdle ? Color.orange : Color.green)
|
.fill(Color.green)
|
||||||
.frame(width: 8, height: 8)
|
.frame(width: 8, height: 8)
|
||||||
Text(appState.isUserIdle ? "空闲" : "活动中")
|
Text("保护中")
|
||||||
.font(.body)
|
.font(.body)
|
||||||
|
|
||||||
// 附加显示 caffeinate 状态
|
|
||||||
if !appState.isUserIdle {
|
|
||||||
Text("(保护中)")
|
|
||||||
.font(.caption2)
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
@@ -112,6 +109,7 @@ struct MenuContent: View {
|
|||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.padding(8)
|
.padding(8)
|
||||||
.background(
|
.background(
|
||||||
@@ -121,6 +119,68 @@ struct MenuContent: View {
|
|||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
|
// 权限管理
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text("权限管理")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
// 打开系统设置按钮
|
||||||
|
Button(action: {
|
||||||
|
// 打开系统设置的自动化页面
|
||||||
|
let settingsUrl = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation")!
|
||||||
|
if NSWorkspace.shared.open(settingsUrl) {
|
||||||
|
print("已打开系统设置自动化页面")
|
||||||
|
} else {
|
||||||
|
// 如果失败,尝试打开通用安全设置
|
||||||
|
let fallbackUrl = URL(string: "x-apple.systempreferences:com.apple.preference.security")!
|
||||||
|
NSWorkspace.shared.open(fallbackUrl)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "gearshape.fill")
|
||||||
|
.frame(width: 20)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
Text("打开系统设置")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
.padding(8)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(Color.blue.opacity(0.05))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
|
||||||
|
|
||||||
|
// 打开用户与群组设置,用于管理登录项
|
||||||
|
Button(action: {
|
||||||
|
let settingsUrl = URL(string: "x-apple.systempreferences:com.apple.preference.usersandgroups?LoginItems")!
|
||||||
|
NSWorkspace.shared.open(settingsUrl)
|
||||||
|
}) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "person.2.fill")
|
||||||
|
.frame(width: 20)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
Text("管理登录项")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundColor(.blue)
|
||||||
|
}
|
||||||
|
.padding(8)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 8)
|
||||||
|
.fill(Color.blue.opacity(0.05))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
}
|
||||||
|
.padding(8)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
// 自动启动选项
|
// 自动启动选项
|
||||||
Toggle(isOn: Binding(
|
Toggle(isOn: Binding(
|
||||||
get: { appState.isLaunchAtLogin },
|
get: { appState.isLaunchAtLogin },
|
||||||
@@ -134,7 +194,7 @@ struct MenuContent: View {
|
|||||||
}
|
}
|
||||||
.toggleStyle(.checkbox)
|
.toggleStyle(.checkbox)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.padding(4)
|
.padding(8)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
@@ -146,7 +206,7 @@ struct MenuContent: View {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.padding(4)
|
.padding(8)
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
@@ -157,7 +217,7 @@ struct MenuContent: View {
|
|||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
.padding(4)
|
.padding(8)
|
||||||
}
|
}
|
||||||
.padding(12)
|
.padding(12)
|
||||||
.frame(minWidth: 280)
|
.frame(minWidth: 280)
|
||||||
@@ -188,6 +248,8 @@ struct MenuContent: View {
|
|||||||
window.title = "PowerBar 使用指南"
|
window.title = "PowerBar 使用指南"
|
||||||
window.contentView = NSHostingView(rootView: GuideView())
|
window.contentView = NSHostingView(rootView: GuideView())
|
||||||
window.center()
|
window.center()
|
||||||
|
|
||||||
|
// 显示窗口
|
||||||
window.makeKeyAndOrderFront(nil)
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
|
||||||
// 记录已显示指引
|
// 记录已显示指引
|
||||||
@@ -195,16 +257,6 @@ struct MenuContent: View {
|
|||||||
|
|
||||||
// 保存窗口引用
|
// 保存窗口引用
|
||||||
guideWindow = window
|
guideWindow = window
|
||||||
|
|
||||||
// 添加窗口关闭通知监听
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
forName: NSWindow.willCloseNotification,
|
|
||||||
object: window,
|
|
||||||
queue: nil
|
|
||||||
) { _ in
|
|
||||||
// 窗口关闭时清除引用
|
|
||||||
guideWindow = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +309,7 @@ struct GuideView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Image(systemName: "power.on.circle")
|
Image(systemName: "power.circle")
|
||||||
.font(.title)
|
.font(.title)
|
||||||
.foregroundColor(.orange)
|
.foregroundColor(.orange)
|
||||||
.frame(width: 40)
|
.frame(width: 40)
|
||||||
@@ -364,6 +416,8 @@ struct GuideView: View {
|
|||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(.blue)
|
.fill(.blue)
|
||||||
)
|
)
|
||||||
|
.transition(.scale(scale: 0.9, anchor: .center))
|
||||||
|
.animation(.easeInOut(duration: 0.2), value: UUID())
|
||||||
}
|
}
|
||||||
.padding(.bottom, 20)
|
.padding(.bottom, 20)
|
||||||
}
|
}
|
||||||
|
|||||||