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")