Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Nook/Components/Peek/PeekOverlayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ struct PeekOverlayView: View {
}
}
.zIndex(9999) // Put it at the very top
.allowsHitTesting(true) // Always allow hit testing
.allowsHitTesting(isActive) // Only intercept events when Peek is active
.onAppear {
// Sync initial state when view appears
if browserManager.peekManager.isActive {
Expand Down
31 changes: 29 additions & 2 deletions Nook/Components/WebsiteView/WebsiteView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ struct WebsiteView: View {
// Critical: Use allowsHitTesting to prevent SwiftUI from intercepting mouse events
// This allows right-clicks to pass through to the underlying NSView (WKWebView)
.allowsHitTesting(true)
.contentShape(Rectangle())
}
// Removed SwiftUI contextMenu - it intercepts ALL right-clicks
// WKWebView's willOpenMenu will handle context menus for images
Expand Down Expand Up @@ -524,6 +523,7 @@ struct TabCompositorWrapper: NSViewRepresentable {
context.coordinator.lastVersion != compositorVersion

if needsRebuild {
let previousCurrentId = context.coordinator.lastCurrentId
updateCompositor(nsView)
context.coordinator.lastIsSplit = isSplit
context.coordinator.lastLeftId = leftId
Expand All @@ -532,6 +532,27 @@ struct TabCompositorWrapper: NSViewRepresentable {
context.coordinator.lastFraction = splitFraction
context.coordinator.lastSize = size
context.coordinator.lastVersion = compositorVersion

// Restore focus when tab changed (webview is now in hierarchy)
if previousCurrentId != currentId {
DispatchQueue.main.async {
guard let window = nsView.window else { return }
for subview in nsView.subviews.reversed() {
if subview is SplitDropCaptureView { continue }
if let webView = subview as? WKWebView, !webView.isHidden {
window.makeFirstResponder(webView)
return
}
// Check pane containers (split view)
for child in subview.subviews {
if let webView = child as? WKWebView, !child.isHidden {
window.makeFirstResponder(webView)
return
}
}
}
}
}
}

// Mark current tab as accessed (resets unload timer)
Expand Down Expand Up @@ -822,7 +843,13 @@ private extension WebsiteView {
private class ContainerView: NSView {
// Don't intercept events - let them pass through to webviews
override var acceptsFirstResponder: Bool { false }


override func resetCursorRects() {
// Empty: prevents NSHostingView and other ancestors from registering
// arrow cursor rects over the webview. WKWebView uses NSCursor.set()
// internally, which works correctly when cursor rects don't override it.
}

// Forward right-clicks to the webview below so context menus work
override func rightMouseDown(with event: NSEvent) {
print("🔽 [ContainerView] rightMouseDown received, forwarding to webview")
Expand Down
17 changes: 1 addition & 16 deletions Nook/Models/BrowserConfig/BrowserConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,22 +111,7 @@ class BrowserConfiguration {

return config
}

// MARK: - User-Agent Client Hints Configuration

/// Configure User-Agent Client Hints for enhanced browser capability reporting
/// This ensures websites receive proper information about platform capabilities
/// including passkey/WebAuthn support
private func configureClientHints(_ config: WKWebViewConfiguration) {
// User-Agent Client Hints are automatically supported when using a Safari-compatible User-Agent
// The applicationNameForUserAgent setting enables this functionality
// Additional headers like Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform are sent automatically

// Enable features that support Client Hints
config.preferences.setValue(true, forKey: "mediaDevicesEnabled")
config.preferences.setValue(true, forKey: "getUserMediaRequiresFocus")
}


// MARK: - Chrome Web Store Integration

/// Get the Web Store injector script
Expand Down
6 changes: 2 additions & 4 deletions Nook/Models/Tab/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3421,9 +3421,9 @@ extension Tab: WKUIDelegate {
completionHandler(true, nil)
}

// MARK: - WebAuthn / Passkey Support
// MARK: - Media Capture Authorization

/// Handle requests for media capture authorization (including WebAuthn/passkey requests)
/// Handle requests for camera/microphone capture authorization
@available(macOS 13.0, *)
public func webView(
_ webView: WKWebView,
Expand All @@ -3436,8 +3436,6 @@ extension Tab: WKUIDelegate {
"🔐 [Tab] Media capture authorization requested for type: \(type.rawValue) from origin: \(origin)"
)

// For passkeys/WebAuthn, we want to grant permission
// The system will handle the actual Touch ID/Face ID prompt
decisionHandler(.grant)
}
}
Expand Down
6 changes: 6 additions & 0 deletions Nook/Nook.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.aps-environment</key>
<string>development</string>
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
<true/>
<key>com.apple.developer.web-browser.public-key-credential</key>
<true/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
Expand Down
22 changes: 11 additions & 11 deletions Nook/Utils/WebKit/FocusableWKWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final class FocusableWKWebView: WKWebView {
}
super.rightMouseDown(with: event)
}

override var acceptsFirstResponder: Bool { true }

override func mouseUp(with event: NSEvent) {
Expand Down Expand Up @@ -101,7 +101,7 @@ final class FocusableWKWebView: WKWebView {
let applied = applyPendingContextMenuIfPossible()
print("🔽 [FocusableWKWebView] prepareMenu: applyPendingContextMenuIfPossible returned \(applied)")
}

func handleImageDownload(identifier: String, promptForLocation: Bool = false) {
let destinationPreference: Download.DestinationPreference = promptForLocation ? .askUser : .automaticDownloadsFolder

Expand All @@ -121,35 +121,35 @@ final class FocusableWKWebView: WKWebView {
}
}
}

private func showSaveDialog(
for localURL: URL,
suggestedFilename: String,
allowedContentTypes: [UTType] = FocusableWKWebView.imageContentTypes
) {
let savePanel = NSSavePanel()
savePanel.nameFieldStringValue = suggestedFilename

// Set the default directory to Downloads
if let downloads = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first {
savePanel.directoryURL = downloads
}

// Set allowed file types for images
savePanel.allowedContentTypes = allowedContentTypes

// Set the title and message
savePanel.title = "Save Image"
savePanel.message = "Choose where to save the image"

// Show the save dialog
savePanel.begin { [weak self] result in
if result == .OK, let destinationURL = savePanel.url {
do {
// Move the downloaded file to the chosen location
try FileManager.default.moveItem(at: localURL, to: destinationURL)
print("🔽 [FocusableWKWebView] Image saved to: \(destinationURL.path)")

// Show a success notification
self?.showSaveSuccessNotification(for: destinationURL)
} catch {
Expand All @@ -162,7 +162,7 @@ final class FocusableWKWebView: WKWebView {
}
}
}

private func showSaveSuccessNotification(for url: URL) {
postUserNotification(
title: "Image Saved",
Expand Down Expand Up @@ -238,9 +238,9 @@ final class FocusableWKWebView: WKWebView {
originalURL: URL,
destinationPreference: Download.DestinationPreference
) {
guard let tab = owningTab else {
guard let tab = owningTab else {
print("🔽 [FocusableWKWebView] No owning tab for download")
return
return
}

var enrichedRequest = request
Expand Down
1 change: 0 additions & 1 deletion Onboarding/Components/TransitionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ struct TransitionView<A: View, B: View>: View {
.frame(width: geo.size.width, height: geo.size.height)
.opacity(renderedShowB ? 1 : 0)
.allowsHitTesting(renderedShowB)
.alwaysArrowCursor()

if let cg = snapshot {
Image(cg, scale: 1, label: Text(""))
Expand Down
Loading