Prechádzať zdrojové kódy

Open app links in an in-app browser window.

Add a WKWebView-backed window for app URLs and enable network client entitlements so pages load under sandbox.

Made-with: Cursor
huzaifahayat12 4 mesiacov pred
rodič
commit
cfc0353f77

+ 2 - 0
google_apps.xcodeproj/project.pbxproj

@@ -248,6 +248,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = google_apps/google_apps.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;
@@ -279,6 +280,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = google_apps/google_apps.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				COMBINE_HIDPI_IMAGES = YES;
 				CURRENT_PROJECT_VERSION = 1;

+ 132 - 0
google_apps/InAppBrowserWindow.swift

@@ -0,0 +1,132 @@
+import AppKit
+import WebKit
+
+final class InAppBrowserWindowManager {
+    static let shared = InAppBrowserWindowManager()
+
+    private var controllers: [InAppBrowserWindowController] = []
+
+    private init() {}
+
+    func open(url: URL, title: String? = nil) {
+        let controller = InAppBrowserWindowController(initialURL: url, windowTitle: title)
+        controllers.append(controller)
+        controller.onClose = { [weak self, weak controller] in
+            guard let self, let controller else { return }
+            self.controllers.removeAll { $0 === controller }
+        }
+        controller.showWindow(nil)
+        controller.window?.makeKeyAndOrderFront(nil)
+    }
+}
+
+final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
+    var onClose: (() -> Void)?
+
+    private let webView: WKWebView
+    private let initialURL: URL
+    private var didLoadInitialURL = false
+
+    init(initialURL: URL, windowTitle: String?) {
+        self.initialURL = initialURL
+
+        let config = WKWebViewConfiguration()
+        config.defaultWebpagePreferences.allowsContentJavaScript = true
+        self.webView = WKWebView(frame: .zero, configuration: config)
+
+        let window = NSWindow(
+            contentRect: NSRect(x: 180, y: 140, width: 1200, height: 800),
+            styleMask: [.titled, .closable, .miniaturizable, .resizable],
+            backing: .buffered,
+            defer: false
+        )
+        window.title = windowTitle ?? "Browser"
+        window.isReleasedWhenClosed = false
+        window.contentView = webView
+
+        super.init(window: window)
+
+        window.delegate = self
+        webView.uiDelegate = self
+        webView.navigationDelegate = self
+        webView.allowsBackForwardNavigationGestures = true
+        webView.setValue(false, forKey: "drawsBackground")
+    }
+
+    @available(*, unavailable)
+    required init?(coder: NSCoder) {
+        nil
+    }
+
+    override func showWindow(_ sender: Any?) {
+        super.showWindow(sender)
+        guard !didLoadInitialURL else { return }
+        didLoadInitialURL = true
+        webView.load(URLRequest(url: initialURL))
+    }
+
+    func windowWillClose(_ notification: Notification) {
+        onClose?()
+    }
+}
+
+extension InAppBrowserWindowController: WKNavigationDelegate {
+    func webView(
+        _ webView: WKWebView,
+        decidePolicyFor navigationAction: WKNavigationAction,
+        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
+    ) {
+        if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
+            InAppBrowserWindowManager.shared.open(url: url)
+            decisionHandler(.cancel)
+            return
+        }
+
+        decisionHandler(.allow)
+    }
+
+    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
+        if let host = webView.url?.host, !host.isEmpty {
+            window?.title = host
+        }
+    }
+
+    func webView(
+        _ webView: WKWebView,
+        didFailProvisionalNavigation navigation: WKNavigation!,
+        withError error: Error
+    ) {
+        showLoadError(error)
+    }
+
+    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
+        showLoadError(error)
+    }
+
+    private func showLoadError(_ error: Error) {
+        let message = """
+        <html>
+        <body style="background:#111;color:#EEE;font-family:-apple-system, sans-serif;padding:24px;">
+        <h2>Page failed to load</h2>
+        <p>Could not open <b>\(initialURL.absoluteString)</b></p>
+        <p>\(error.localizedDescription)</p>
+        </body>
+        </html>
+        """
+        webView.loadHTMLString(message, baseURL: nil)
+        window?.title = "Load Error"
+    }
+}
+
+extension InAppBrowserWindowController: WKUIDelegate {
+    func webView(
+        _ webView: WKWebView,
+        createWebViewWith configuration: WKWebViewConfiguration,
+        for navigationAction: WKNavigationAction,
+        windowFeatures: WKWindowFeatures
+    ) -> WKWebView? {
+        guard let url = navigationAction.request.url else { return nil }
+        InAppBrowserWindowManager.shared.open(url: url)
+        return nil
+    }
+}

+ 2 - 2
google_apps/LauncherRootView.swift

@@ -43,7 +43,7 @@ struct LauncherRootView: View {
                     },
                     onOpenGoogle: {
                         if let url = URL(string: "https://www.google.com") {
-                            NSWorkspace.shared.open(url)
+                            InAppBrowserWindowManager.shared.open(url: url, title: "Google")
                         }
                     }
                 )
@@ -105,7 +105,7 @@ struct LauncherRootView: View {
 
     private func handleAppTap(_ app: LauncherApp) {
         if let webURL = app.webURL {
-            NSWorkspace.shared.open(webURL)
+            InAppBrowserWindowManager.shared.open(url: webURL, title: app.name)
             return
         }
 

+ 10 - 0
google_apps/google_apps.entitlements

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!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.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.network.client</key>
+	<true/>
+</dict>
+</plist>