Mac 剪贴板自动下行(远端→本机)+ iOS 忽略 auto(Phase 4b·2)
- 共享引擎 JS(web/src/features/clipboard/clipboard.ts):applyRemoteClipboard(SSE clipboard:update / fetchClipboard)在原生壳(isIOSShell)时对远端更新附加 notifyNative("clipboard", { content, sourceDevice, auto:true }),把下行同步过桥给原生。平台差异交原生侧处理,JS 不读 device_type(surface 更小、iOS 无回归)
- 原生 handleNotify("clipboard"):读 auto 标志。macOS 写系统剪贴板(先 armSelfWrite 防自动上行回环)且 auto 时不弹提示(避免远端每次变化刷屏);iOS 忽略 auto(后台受限 + 隐私,不自动写系统剪贴板),仅手动拉取(auto=false)写入并提示。手动拉取两端行为不变
- 至此 Mac 剪贴板双向自动同步齐全:上行(MacClipboardMonitor 监听本机复制 → 上传)+ 下行(远端 SSE → 自动写本机)
- **须 prod 部署 web 引擎**方在正式包生效(Mac 从 drop.commilitia.net 加载 engine.html);本地 CDROP_ENGINE_URL 指向本机 web dev server 可即时验证
- 验证:just typecheck-front(tsc 通过)+ just mac-build(CommilitiaDropMac.app)+ iOS 模拟器(CommilitiaDrop.app)均 BUILD SUCCEEDED
This commit is contained in:
@@ -1012,20 +1012,29 @@ extension EngineController: WKScriptMessageHandler
|
|||||||
if logs.count > Self.logsCap { logs.removeLast(logs.count - Self.logsCap) }
|
if logs.count > Self.logsCap { logs.removeLast(logs.count - Self.logsCap) }
|
||||||
}
|
}
|
||||||
case "clipboard":
|
case "clipboard":
|
||||||
// 引擎拉回的云剪贴板内容 → 写入本机 UIPasteboard。
|
// 云剪贴板内容 → 写入本机剪贴板。auto=true 为远端自动下行(JS 在 isIOSShell 时对 clipboard:update
|
||||||
if let p = payload as? [String: Any], let content = p["content"] as? String, !content.isEmpty
|
// 附加):iOS 不自动写系统剪贴板(后台受限 + 隐私,仍走控制中心手动拉取),故忽略 auto;macOS 写入
|
||||||
|
// 且不弹提示(避免每次远端变化刷屏)。auto=false 为用户手动拉取:两端都写并提示。
|
||||||
|
let clip = payload as? [String: Any]
|
||||||
|
let clipContent = clip?["content"] as? String ?? ""
|
||||||
|
let clipAuto = (clip?["auto"] as? Bool) ?? false
|
||||||
|
if !clipContent.isEmpty
|
||||||
{
|
{
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
// 先武装自写守卫(令剪贴板自动同步 monitor 认出刚拉取写入的内容为自身回声、不再上传),再写。
|
// 先武装自写守卫(令自动同步 monitor 认出刚写入的内容为自身回声、不再上传),再写。
|
||||||
MacClipboardMonitor.shared.armSelfWrite(content)
|
MacClipboardMonitor.shared.armSelfWrite(clipContent)
|
||||||
NSPasteboard.general.clearContents()
|
NSPasteboard.general.clearContents()
|
||||||
NSPasteboard.general.setString(content, forType: .string)
|
NSPasteboard.general.setString(clipContent, forType: .string)
|
||||||
|
if !clipAuto { notify(t("ios.clipboard.pulled"), kind: .success) }
|
||||||
#else
|
#else
|
||||||
UIPasteboard.general.string = content
|
if !clipAuto
|
||||||
#endif
|
{
|
||||||
|
UIPasteboard.general.string = clipContent
|
||||||
notify(t("ios.clipboard.pulled"), kind: .success)
|
notify(t("ios.clipboard.pulled"), kind: .success)
|
||||||
}
|
}
|
||||||
else
|
#endif
|
||||||
|
}
|
||||||
|
else if !clipAuto
|
||||||
{
|
{
|
||||||
notify(t("ios.clipboard.empty"), kind: .info)
|
notify(t("ios.clipboard.empty"), kind: .info)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { apiFetch } from "../../net/api";
|
import { apiFetch } from "../../net/api";
|
||||||
import { writeNativeClipboard } from "../../net/desktop";
|
import { writeNativeClipboard } from "../../net/desktop";
|
||||||
|
import { isIOSShell, notifyNative } from "../../net/ios";
|
||||||
import { useAppStore, type ClipboardState } from "../../store";
|
import { useAppStore, type ClipboardState } from "../../store";
|
||||||
import { t } from "../../i18n";
|
import { t } from "../../i18n";
|
||||||
|
|
||||||
@@ -181,6 +182,16 @@ export function applyRemoteClipboard(state: ClipboardState): void
|
|||||||
if (!selfDevice || state.sourceDevice !== selfDevice)
|
if (!selfDevice || state.sourceDevice !== selfDevice)
|
||||||
{
|
{
|
||||||
writeNativeClipboard(state.content, state.sourceDevice);
|
writeNativeClipboard(state.content, state.sourceDevice);
|
||||||
|
// 原生壳(iOS/Mac WebView 引擎):远端剪贴板更新过桥给原生(下行自动同步)。平台差异由原生侧
|
||||||
|
// 处理——macOS 静默写系统剪贴板,iOS 忽略 auto(后台受限 + 隐私,仍走控制中心手动拉取)。
|
||||||
|
if (isIOSShell())
|
||||||
|
{
|
||||||
|
notifyNative("clipboard", {
|
||||||
|
content: state.content,
|
||||||
|
sourceDevice: state.sourceDevice,
|
||||||
|
auto: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user