Files
pangolin/client/macos/Runner/MainFlutterWindow.swift
wangjia cf19ca2d34 feat(client/macos): 开机自启用 SMAppService + LoginItem helper(学 Tailscale)
根因:launch_at_startup 0.5.1 在 macOS 无自带实现,需宿主接 MethodChannel,而我们 Runner
从没实现 → enable() 抛 MissingPluginException 被 try/catch 吞掉 → 登录项从未注册,自启失败。
学 Tailscale(LoginItemHelper-macsys + SMAppService):
- 新增无界面 LoginItem helper(macos/login_helper,LSBackgroundOnly),登录时带 --autostart
  拉起主 app(→隐藏到托盘);
- build_login_helper.sh 构建期编译+签名+嵌入 Contents/Library/LoginItems/(新 Run Script 阶段);
- MainFlutterWindow 接 launch_at_startup MethodChannel → SMAppService.loginItem 注册/注销。
构建通过:helper 嵌入+签名,主 app --deep --strict 校验过。Windows 自启(schtasks)不变。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 10:25:27 +08:00

71 lines
3.0 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Cocoa
import FlutterMacOS
import ServiceManagement
class MainFlutterWindow: NSWindow {
// launch_at_startup macOS ,宿 MethodChannel Tailscale
// SMAppService.loginItem PangolinLoginHelper( --autostart app)
private let loginHelperID = "com.pangolin.pangolin.LoginHelper"
override func awakeFromNib() {
let flutterViewController = FlutterViewController()
self.contentViewController = flutterViewController
// 920×600稿 ui_kits/desktop 720×560
let defaultSize = NSSize(width: 920, height: 600)
self.setContentSize(defaultSize)
self.contentMinSize = NSSize(width: 720, height: 560)
self.center()
RegisterGeneratedPlugins(registry: flutterViewController)
// P1 B:VPN (NETunnelProviderManager PacketTunnel)
// VpnChannel.swift Runner target Compile Sources
VpnChannel.register(with: flutterViewController.registrar(forPlugin: "VpnChannel"))
// MethodChannel(launch_at_startup ,macOS 宿)
registerLaunchAtStartupChannel(messenger: flutterViewController.engine.binaryMessenger)
super.awakeFromNib()
// ( --autostart):,( Windows )
// Dart main() windowManager.hide(); orderOut , storyboard
// "Visible At Launch" ()
if CommandLine.arguments.contains("--autostart") {
self.orderOut(nil)
}
}
// launch_at_startup macOS :SMAppService.loginItem / LoginHelper
private func registerLaunchAtStartupChannel(messenger: FlutterBinaryMessenger) {
let channel = FlutterMethodChannel(name: "launch_at_startup", binaryMessenger: messenger)
let helperID = loginHelperID
channel.setMethodCallHandler { call, result in
guard #available(macOS 13.0, *) else {
result(FlutterError(code: "unsupported", message: "开机自启需 macOS 13+", details: nil))
return
}
let svc = SMAppService.loginItem(identifier: helperID)
switch call.method {
case "launchAtStartupIsEnabled":
result(svc.status == .enabled)
case "launchAtStartupSetEnabled":
let on = ((call.arguments as? [String: Any])?["setEnabledValue"] as? Bool) ?? false
do {
if on {
if svc.status != .enabled { try svc.register() }
} else {
if svc.status == .enabled { try svc.unregister() }
}
result(nil)
} catch {
NSLog("[pangolin/launch] SMAppService %@ failed: %@", on ? "register" : "unregister", error.localizedDescription)
result(FlutterError(code: "smappservice", message: error.localizedDescription, details: nil))
}
default:
result(FlutterMethodNotImplemented)
}
}
}
}