From 4f477c4ed076da98c54fae9eafa8ca85fb5188f7 Mon Sep 17 00:00:00 2001 From: bbblending Date: Thu, 18 Jun 2026 15:45:18 +0800 Subject: [PATCH] fix(macos): open system file picker for in Control UI --- .../CanvasWindowController+UIDelegate.swift | 32 +++++++++++++++++++ .../OpenClaw/CanvasWindowController.swift | 3 +- .../OpenClaw/DashboardWindowController.swift | 30 ++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 apps/macos/Sources/OpenClaw/CanvasWindowController+UIDelegate.swift diff --git a/apps/macos/Sources/OpenClaw/CanvasWindowController+UIDelegate.swift b/apps/macos/Sources/OpenClaw/CanvasWindowController+UIDelegate.swift new file mode 100644 index 000000000000..33bd1439a092 --- /dev/null +++ b/apps/macos/Sources/OpenClaw/CanvasWindowController+UIDelegate.swift @@ -0,0 +1,32 @@ +import AppKit +import WebKit + +extension CanvasWindowController { + // MARK: - WKUIDelegate + + /// Bridges `` clicks in canvas HTML to a native `NSOpenPanel`. + /// Without a `WKUIDelegate`, WebKit silently drops the request and file-picker + /// buttons in canvas pages do nothing. + @MainActor + func webView( + _ webView: WKWebView, + runOpenPanelWith parameters: WKOpenPanelParameters, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping @MainActor @Sendable ([URL]?) -> Void) + { + let panel = NSOpenPanel() + panel.canChooseFiles = true + panel.canChooseDirectories = parameters.allowsDirectories + panel.allowsMultipleSelection = parameters.allowsMultipleSelection + panel.resolvesAliases = true + if let window = self.window { + panel.beginSheetModal(for: window) { response in + completionHandler(response == .OK ? panel.urls : nil) + } + return + } + panel.begin { response in + completionHandler(response == .OK ? panel.urls : nil) + } + } +} diff --git a/apps/macos/Sources/OpenClaw/CanvasWindowController.swift b/apps/macos/Sources/OpenClaw/CanvasWindowController.swift index df5cb4fe2ff7..9ad31703da8c 100644 --- a/apps/macos/Sources/OpenClaw/CanvasWindowController.swift +++ b/apps/macos/Sources/OpenClaw/CanvasWindowController.swift @@ -5,7 +5,7 @@ import OpenClawKit import WebKit @MainActor -final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NSWindowDelegate { +final class CanvasWindowController: NSWindowController, WKNavigationDelegate, WKUIDelegate, NSWindowDelegate { let sessionKey: String private let root: URL private let sessionDir: URL @@ -159,6 +159,7 @@ final class CanvasWindowController: NSWindowController, WKNavigationDelegate, NS } self.webView.navigationDelegate = self + self.webView.uiDelegate = self self.window?.delegate = self self.container.onClose = { [weak self] in self?.hideCanvas() diff --git a/apps/macos/Sources/OpenClaw/DashboardWindowController.swift b/apps/macos/Sources/OpenClaw/DashboardWindowController.swift index 9bb2f080f219..1bfd610631c7 100644 --- a/apps/macos/Sources/OpenClaw/DashboardWindowController.swift +++ b/apps/macos/Sources/OpenClaw/DashboardWindowController.swift @@ -19,7 +19,7 @@ private final class DashboardWindowDragRegionView: NSView { } @MainActor -final class DashboardWindowController: NSWindowController, WKNavigationDelegate, NSWindowDelegate { +final class DashboardWindowController: NSWindowController, WKNavigationDelegate, WKUIDelegate, NSWindowDelegate { private let webView: WKWebView private var currentURL: URL private var auth: DashboardWindowAuth @@ -44,9 +44,37 @@ final class DashboardWindowController: NSWindowController, WKNavigationDelegate, super.init(window: window) self.webView.navigationDelegate = self + self.webView.uiDelegate = self self.window?.delegate = self } + // MARK: - WKUIDelegate + + /// Bridges `` clicks in the embedded Control UI to a native + /// `NSOpenPanel`; without a `WKUIDelegate`, WebKit silently drops the request + /// and "Choose image" / file-picker buttons do nothing. + func webView( + _ webView: WKWebView, + runOpenPanelWith parameters: WKOpenPanelParameters, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping @MainActor @Sendable ([URL]?) -> Void) + { + let panel = NSOpenPanel() + panel.canChooseFiles = true + panel.canChooseDirectories = parameters.allowsDirectories + panel.allowsMultipleSelection = parameters.allowsMultipleSelection + panel.resolvesAliases = true + if let window = self.window { + panel.beginSheetModal(for: window) { response in + completionHandler(response == .OK ? panel.urls : nil) + } + return + } + panel.begin { response in + completionHandler(response == .OK ? panel.urls : nil) + } + } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) is not supported")