Add an iPhone client sharing the macOS app's SwiftUI code
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import AppKit
|
||||
import Foundation
|
||||
import Network
|
||||
import Observation
|
||||
@@ -268,6 +267,7 @@ final class Pages {
|
||||
return String(describing: value)
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
func capture(_ view: WKWebView) async throws -> Data {
|
||||
guard unsafe view.window != nil, !view.isHiddenOrHasHiddenAncestor else {
|
||||
throw ApiError(status: 0, message: "the tab isn't on screen: the human has the page view closed or another tab showing")
|
||||
@@ -278,6 +278,7 @@ final class Pages {
|
||||
}
|
||||
return png
|
||||
}
|
||||
#endif
|
||||
|
||||
func close() {
|
||||
listener?.close()
|
||||
@@ -546,34 +547,67 @@ private final class PageDelegate: NSObject, WKUIDelegate, WKNavigationDelegate {
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo) async {
|
||||
_ = await ask(webView, frame, message, buttons: ["OK"], field: nil)
|
||||
_ = await ask(webView, frame, message, cancel: false, field: nil)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo) async -> Bool {
|
||||
await ask(webView, frame, message, buttons: ["OK", "Cancel"], field: nil) == .alertFirstButtonReturn
|
||||
await ask(webView, frame, message, cancel: true, field: nil) != nil
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo) async -> String? {
|
||||
let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
|
||||
field.stringValue = defaultText ?? ""
|
||||
guard await ask(webView, frame, prompt, buttons: ["OK", "Cancel"], field: field) == .alertFirstButtonReturn else {
|
||||
return nil
|
||||
}
|
||||
return field.stringValue
|
||||
await ask(webView, frame, prompt, cancel: true, field: defaultText ?? "")
|
||||
}
|
||||
|
||||
private func ask(_ webView: WKWebView, _ frame: WKFrameInfo, _ message: String, buttons: [String], field: NSTextField?) async -> NSApplication.ModalResponse {
|
||||
// Returns nil when the human cancels, otherwise the field's text ("" without a field).
|
||||
private func ask(_ webView: WKWebView, _ frame: WKFrameInfo, _ message: String, cancel: Bool, field: String?) async -> String? {
|
||||
let title = frame.securityOrigin.host.isEmpty ? "This page says" : "\(frame.securityOrigin.host) says"
|
||||
#if os(macOS)
|
||||
guard let window = unsafe webView.window, !webView.isHiddenOrHasHiddenAncestor else {
|
||||
return .cancel
|
||||
return nil
|
||||
}
|
||||
let alert = NSAlert()
|
||||
alert.messageText = frame.securityOrigin.host.isEmpty ? "This page says" : "\(frame.securityOrigin.host) says"
|
||||
alert.messageText = title
|
||||
alert.informativeText = message
|
||||
for title in buttons {
|
||||
alert.addButton(withTitle: title)
|
||||
alert.addButton(withTitle: "OK")
|
||||
if cancel {
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
}
|
||||
alert.accessoryView = field
|
||||
return await alert.beginSheetModal(for: window)
|
||||
let input = field.map { text in
|
||||
let f = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
|
||||
f.stringValue = text
|
||||
return f
|
||||
}
|
||||
alert.accessoryView = input
|
||||
guard await alert.beginSheetModal(for: window) == .alertFirstButtonReturn else {
|
||||
return nil
|
||||
}
|
||||
return input?.stringValue ?? ""
|
||||
#else
|
||||
guard let window = webView.window, !webView.isHidden, var top = window.rootViewController else {
|
||||
return nil
|
||||
}
|
||||
while let presented = top.presentedViewController {
|
||||
top = presented
|
||||
}
|
||||
let presenter = top
|
||||
return await withCheckedContinuation { c in
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
if let field {
|
||||
alert.addTextField { f in
|
||||
f.text = field
|
||||
}
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default) { [weak alert] _ in
|
||||
c.resume(returning: alert?.textFields?.first?.text ?? "")
|
||||
})
|
||||
if cancel {
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
|
||||
c.resume(returning: nil)
|
||||
})
|
||||
}
|
||||
presenter.present(alert, animated: true)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
@@ -589,7 +623,8 @@ private final class PageDelegate: NSObject, WKUIDelegate, WKNavigationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private struct WebPage: NSViewRepresentable {
|
||||
@MainActor
|
||||
private struct WebPage {
|
||||
let view: WKWebView
|
||||
let reloads: Int
|
||||
let shown: Bool
|
||||
@@ -603,20 +638,41 @@ private struct WebPage: NSViewRepresentable {
|
||||
Coordinator()
|
||||
}
|
||||
|
||||
func makeNSView(context: Context) -> WKWebView {
|
||||
context.coordinator.reloads = reloads
|
||||
private func make(_ c: Coordinator) -> WKWebView {
|
||||
c.reloads = reloads
|
||||
if refresh, !view.isLoading {
|
||||
view.reload()
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
func updateNSView(_ view: WKWebView, context: Context) {
|
||||
private func update(_ view: WKWebView, _ c: Coordinator) {
|
||||
view.isHidden = !shown
|
||||
let c = context.coordinator
|
||||
if c.reloads != reloads {
|
||||
c.reloads = reloads
|
||||
view.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
extension WebPage: NSViewRepresentable {
|
||||
func makeNSView(context: Context) -> WKWebView {
|
||||
make(context.coordinator)
|
||||
}
|
||||
|
||||
func updateNSView(_ view: WKWebView, context: Context) {
|
||||
update(view, context.coordinator)
|
||||
}
|
||||
}
|
||||
#else
|
||||
extension WebPage: UIViewRepresentable {
|
||||
func makeUIView(context: Context) -> WKWebView {
|
||||
make(context.coordinator)
|
||||
}
|
||||
|
||||
func updateUIView(_ view: WKWebView, context: Context) {
|
||||
update(view, context.coordinator)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user