原生 macOS 引擎 + Broker 登录接通(Phase 3a)

- 引擎 / 鉴权跨平台 in-place(iOS 路径 #if 守卫、逐字不变,仅加 macOS/AppKit 分支):EngineController(import 守卫 + 剪贴板 UIPasteboard→NSPasteboard 上下行两处 + device_type 平台条件)、EngineWebView(UIViewRepresentable→typealias 统一后分 NSViewRepresentable)、Theme(动态色 Color(nsColor:dynamicProvider) 同 RGB)、DeviceNameStore(Host.current().localizedName)、PushRegistry(NSApplication.registerForRemoteNotifications)、Auth/BrokerLogin(presentationAnchor→NSApplication.keyWindow)、Auth/AuthManager(死 import UIKit 守卫)
- BackgroundTaskManager:整文件 #if os(iOS)(BGContinuedProcessingTask 为 API_UNAVAILABLE(macos)、编译级拒绝)+ macOS 同签名 no-op stub,使 EngineController.syncBackgroundTask 调用点无须改动
- device_type 修正(非机械):Mac 原生客户端 boot 注入 device_type=macos(与桌面 Wails 客户端一致,后端 / 各端已识别),不再硬编 ios——否则其他端设备列表图标错、且触发其 isWakeable 的 contains("ios") 误判本 Mac 可推送唤醒
- CommilitiaDropMac target 纳入「引擎 + 登录」最小集:Shared + Sources/Engine + Sources/Auth(除 iOS 专属 LoginView)+ RecordsStore / PushRegistry / LocalNetworkPermission / DeviceNameStore / BackgroundTaskManager + Resources/i18n;依赖 WebRTC;Info.plist 加 cdrop:// 回调 scheme(ASWebAuthenticationSession OAuth 回调)+ 本地网络(NSLocalNetworkUsageDescription / NSBonjourServices _cdrop._tcp)
- Mac 壳接引擎:CommilitiaDropMacApp(@State auth + engine,MacAppRoot 登录门 + 会话激活分桶记录)、MacLoginView(Broker 账户登录,免相机)、MacRootView(背景挂离屏引擎 WebView 保活 + 设备真实 presence 列表 + 设置账号/登出/引擎日志)、MacMenuContent(账号 + 在线设备数)。传输/文件/消息详情暂占位,随 Phase 3b 复用 iOS 视图
- 验证:just mac-build(CommilitiaDropMac.app)+ iOS 模拟器(CommilitiaDrop.app)均 BUILD SUCCEEDED。运行期登录 / 引擎联通待真机自测(Broker OAuth 需账号交互)
This commit is contained in:
2026-07-07 22:22:30 +08:00
parent 9d46e26f50
commit 512d7563ee
14 changed files with 378 additions and 20 deletions
@@ -1,6 +1,10 @@
import Foundation
import Observation
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
import WebKit
// presence web store DeviceInfolastSeen ms epoch
@@ -275,12 +279,18 @@ final class EngineController: NSObject
let ucc = WKUserContentController()
ucc.add(self, name: "cdropEngine")
// boot nulldevice_type ios
// api_base /api prod URL
// boot nulldevice_type /
// + isWakeable api_base /api prod
// URL iOS "ios" Mac "macos" Wails /
let debug = ProcessInfo.processInfo.environment["CDROP_DEBUG_SESSION"] == "1"
#if os(macOS)
let deviceType = "macos"
#else
let deviceType = "ios"
#endif
let boot = "window.__CDROP_BOOT__ = { session: \(sessionJSON()), "
+ "device_name: \(jsString(currentDeviceName())), "
+ "api_base: \(jsString(apiBaseForBoot())), device_type: \"ios\", debug: \(debug) };"
+ "api_base: \(jsString(apiBaseForBoot())), device_type: \(jsString(deviceType)), debug: \(debug) };"
ucc.addUserScript(WKUserScript(source: boot,
injectionTime: .atDocumentStart,
forMainFrameOnly: true))
@@ -539,7 +549,11 @@ final class EngineController: NSObject
// UIPasteboard
func uploadClipboard()
{
#if os(macOS)
let content = NSPasteboard.general.string(forType: .string) ?? ""
#else
let content = UIPasteboard.general.string ?? ""
#endif
guard !content.isEmpty else { notify(t("ios.clipboard.empty"), kind: .info); return }
sendCommand("clipboardUpload", payload: [ "content": content ])
}
@@ -982,7 +996,12 @@ extension EngineController: WKScriptMessageHandler
// UIPasteboard
if let p = payload as? [String: Any], let content = p["content"] as? String, !content.isEmpty
{
#if os(macOS)
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(content, forType: .string)
#else
UIPasteboard.general.string = content
#endif
notify(t("ios.clipboard.pulled"), kind: .success)
}
else