From f7b0f04c9c056219c93a7cac539de3ee43b137ce Mon Sep 17 00:00:00 2001 From: commilitia Date: Sat, 11 Jul 2026 12:49:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=20ultracode=20=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E7=9A=84=20Quick=20Send=20=E5=85=B3=E7=AA=97?= =?UTF-8?q?=E9=9D=99=E9=BB=98=E5=A4=B1=E6=95=88=20+=20=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=88=A4=E6=9C=AC=E6=9C=BA=E5=9B=9E=E8=90=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Quick Send 关主窗口静默失效(ultracode medium,对抗核验为真):MacQuickSend.trigger 只写 pending,而观察 pending 弹发送表单的逻辑全挂在 MacRootView——后台 / 菜单栏常驻(关主窗口、隐藏 Dock)时 MacRootView 未挂载,选完文件无表单、零反馈,正是 Quick Send 的首要场景。修:MacQuickSend 改为自持独立 NSWindow(NSHostingController 托 MacComposeSheet)呈现发送面板,不依赖主窗口——关窗 / 隐藏 Dock 态下热键 ⌃⌥⌘S 与菜单栏「快速发送文件…」均正常。MacRootView 移除 pending 的 onChange + sheet(不再需要) - 设备判本机回落(ultracode 核验 real=false,Mac 上不可达;作防御性对齐 iOS):MacDevicesView.others 由只比 deviceID != selfDeviceID 改为 isSelf——selfDeviceID 非空按 device_id 判、缺失回落按设备名(对齐 iOS isSelf),防边角态下本机误入「其他设备」列表被右键「移除」而吊销自身会话 - 未改:Keychain ad-hoc 重编后 ACL 失效(ultracode low)——纯本地开发内环摩擦(改一行重编即换 cdhash),Phase 6 Developer ID 稳定签名后自消,对最终用户零影响、无安全/正确性后果 - 扫描结论:6 维度里剪贴板安全 / iOS 回归 / 共享 JS 变更 / 后台生命周期 四项 finder 零发现(对抗核验通过);仅 mac-features + auth-build-config 各出一条,核验为真 2 条(本次修 1 + 防御 1)、驳回 1 - 验证:just mac-build(CommilitiaDropMac.app)BUILD SUCCEEDED;iOS 无关(仅改 macOS-only 文件) --- ios/CDrop/MacApp/MacQuickSend.swift | 63 ++++++++++++++++++++++------- ios/CDrop/MacApp/MacRootView.swift | 30 +++++--------- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/ios/CDrop/MacApp/MacQuickSend.swift b/ios/CDrop/MacApp/MacQuickSend.swift index 4916ae3..a145d46 100644 --- a/ios/CDrop/MacApp/MacQuickSend.swift +++ b/ios/CDrop/MacApp/MacQuickSend.swift @@ -1,31 +1,66 @@ #if os(macOS) import AppKit -import Observation +import SwiftUI -// Quick Send:全局热键(MacHotkey)或菜单栏触发 → 激活 app + NSOpenPanel 选文件 → 主窗口切到「传输」区 -// 并弹发送表单选目标设备发送。pending 为待发文件,MacRootView 观察它、présente MacComposeSheet。 -@Observable +// Quick Send:全局热键(MacHotkey)或菜单栏触发 → 激活 app + NSOpenPanel 选文件 → 弹出独立发送面板 +// (自持 NSWindow 托 MacComposeSheet)选目标设备发送。**自持面板而非挂主窗口的 sheet**:Quick Send 的 +// 典型场景正是「关主窗口、仅菜单栏 / 热键常驻」,此时主窗口 MacRootView 未挂载、其 sheet 无处可弹(会 +// 静默失效)——故此处不依赖主窗口,直接以自己的窗口呈现发送表单(ultracode 审查修复)。 +@MainActor final class MacQuickSend { static let shared = MacQuickSend() private init() {} - var pending: [URL] = [] + private var panel: NSWindow? - // 激活 app + 选文件(主线程同步)。有选中即置 pending,触发主窗口的发送表单。未登录时 MacRootView - // 未挂载、表单不出现——Quick Send 仅登录态有意义(菜单项 / 热键均在登录后才提供 / 注册)。 + // 激活 app + 选文件(主线程同步)。有选中即弹发送面板;无可达设备则提示。 static func trigger() { NSApp.activate() - let panel = NSOpenPanel() - panel.canChooseFiles = true - panel.canChooseDirectories = false - panel.allowsMultipleSelection = true - panel.prompt = "发送" - if panel.runModal() == .OK, !panel.urls.isEmpty + let p = NSOpenPanel() + p.canChooseFiles = true + p.canChooseDirectories = false + p.allowsMultipleSelection = true + p.prompt = "发送" + guard p.runModal() == .OK, !p.urls.isEmpty else { return } + shared.present(urls: p.urls) + } + + private func present(urls: [URL]) + { + let engine = MacBackgroundController.shared.engine + guard !engine.sendableDevices().isEmpty else { - shared.pending = panel.urls + let alert = NSAlert() + alert.messageText = "暂无可达设备" + alert.informativeText = "请确保有其他在线设备(或可唤醒的 iOS 设备)。" + alert.runModal() + return } + // 复用 MacComposeSheet(待发文件 + 目标设备选择);发送 / 取消后关面板。 + let root = MacComposeSheet(urls: urls, + onCancel: { [weak self] in self?.close() }, + onSend: + { [weak self] device in + for url in urls { engine.sendFile(to: device, fileURL: url) } + self?.close() + }) + .environment(engine) + let win = NSWindow(contentViewController: NSHostingController(rootView: root)) + win.title = "快速发送" + win.styleMask = [ .titled, .closable ] + win.isReleasedWhenClosed = false + win.center() + win.makeKeyAndOrderFront(nil) + NSApp.activate() + panel = win + } + + private func close() + { + panel?.close() + panel = nil } } #endif diff --git a/ios/CDrop/MacApp/MacRootView.swift b/ios/CDrop/MacApp/MacRootView.swift index 04f6408..efa67a6 100644 --- a/ios/CDrop/MacApp/MacRootView.swift +++ b/ios/CDrop/MacApp/MacRootView.swift @@ -10,7 +10,6 @@ struct MacRootView: View { @Environment(EngineController.self) private var engine @Environment(AuthManager.self) private var auth - @State private var quickSend = MacQuickSend.shared @State private var selection: MacSection = .transfers var body: some View @@ -51,23 +50,7 @@ struct MacRootView: View { NoticeOverlay() } - // Quick Send(热键 / 菜单栏触发):有待发文件即切到「传输」并弹发送表单。 - .onChange(of: quickSend.pending) - { _, urls in - if !urls.isEmpty { selection = .transfers } - } - .sheet(isPresented: Binding(get: { !quickSend.pending.isEmpty }, - set: { if !$0 { quickSend.pending = [] } })) - { - MacComposeSheet(urls: quickSend.pending, - onCancel: { quickSend.pending = [] }, - onSend: - { device in - for url in quickSend.pending { engine.sendFile(to: device, fileURL: url) } - quickSend.pending = [] - }) - .environment(engine) - } + // Quick Send(热键 / 菜单栏触发)自持独立发送面板(见 MacQuickSend),不依赖本窗口,故此处无需挂载。 } @ViewBuilder @@ -155,7 +138,16 @@ struct MacDevicesView: View private var others: [DeviceItem] { - engine.devices.filter { $0.deviceID != engine.selfDeviceID } + engine.devices.filter { !isSelf($0) } + } + + // 判本机:优先按稳定 device_id,selfDeviceID 缺失时回落按设备名(对齐 iOS isSelf,防边角态下本机 + // 误入「其他设备」列表被右键「移除」而吊销自身会话)。 + private func isSelf(_ dev: DeviceItem) -> Bool + { + let selfID = engine.selfDeviceID + if !selfID.isEmpty, !dev.deviceID.isEmpty { return dev.deviceID == selfID } + return dev.name == engine.deviceName } var body: some View