import SwiftUI import WebKit /// Montana Video — Video content browser struct VideoView: View { @EnvironmentObject var engine: PresenceEngine @State private var showSidebar = false @State private var currentTitle = "Montana Video" @State private var isLoading = true @State private var canGoBack = false @State private var canGoForward = false private let gold = Color(red: 0.85, green: 0.68, blue: 0.25) private let videoURL = "https://efir.org" var body: some View { ZStack(alignment: .leading) { VStack(spacing: 0) { // ── BURGER MENU BUTTON ── HStack { Button(action: { withAnimation(.easeInOut(duration: 0.3)) { showSidebar = true } }) { Image(systemName: "line.3.horizontal") .font(.system(size: 18, weight: .medium)) .foregroundColor(gold) .padding(8) } .buttonStyle(.plain) Spacer() } .padding(.horizontal, 12) .padding(.top, 8) // Navigation bar HStack(spacing: 8) { Button(action: { NotificationCenter.default.post(name: .videoViewGoBack, object: nil) }) { Image(systemName: "chevron.left") .font(.system(size: 14, weight: .medium)) .foregroundColor(canGoBack ? gold : .secondary.opacity(0.4)) } .buttonStyle(.plain) .disabled(!canGoBack) Button(action: { NotificationCenter.default.post(name: .videoViewGoForward, object: nil) }) { Image(systemName: "chevron.right") .font(.system(size: 14, weight: .medium)) .foregroundColor(canGoForward ? gold : .secondary.opacity(0.4)) } .buttonStyle(.plain) .disabled(!canGoForward) Button(action: { NotificationCenter.default.post(name: .videoViewReload, object: nil) }) { Image(systemName: isLoading ? "xmark" : "arrow.clockwise") .font(.system(size: 12, weight: .medium)) .foregroundColor(gold) } .buttonStyle(.plain) // Title bar HStack(spacing: 6) { Image(systemName: "play.circle.fill") .font(.system(size: 12)) .foregroundColor(Color(red: 0.48, green: 0.18, blue: 1.0)) Text(currentTitle) .font(.system(size: 12)) .foregroundColor(.primary) .lineLimit(1) .truncationMode(.tail) } .padding(.horizontal, 10) .padding(.vertical, 6) .frame(maxWidth: .infinity, alignment: .leading) .background(Color(NSColor.controlBackgroundColor)) .cornerRadius(8) Button(action: { NotificationCenter.default.post(name: .videoViewLoadURL, object: videoURL) }) { Image(systemName: "house.fill") .font(.system(size: 14)) .foregroundColor(gold) } .buttonStyle(.plain) } .padding(.horizontal, 12) .padding(.vertical, 6) Divider() // WebView for video content VideoWebView( urlString: videoURL, currentTitle: $currentTitle, isLoading: $isLoading, canGoBack: $canGoBack, canGoForward: $canGoForward ) } .background(Color(NSColor.windowBackgroundColor)) // Sidebar overlay if showSidebar { Color.black.opacity(0.3) .ignoresSafeArea() .onTapGesture { withAnimation(.easeInOut(duration: 0.3)) { showSidebar = false } } SharedSidebar(isVisible: $showSidebar) .transition(.move(edge: .leading)) } } } } // MARK: - Video WebView Notifications extension Notification.Name { static let videoViewGoBack = Notification.Name("videoViewGoBack") static let videoViewGoForward = Notification.Name("videoViewGoForward") static let videoViewReload = Notification.Name("videoViewReload") static let videoViewLoadURL = Notification.Name("videoViewLoadURL") } // MARK: - Video WKWebView Wrapper struct VideoWebView: NSViewRepresentable { let urlString: String @Binding var currentTitle: String @Binding var isLoading: Bool @Binding var canGoBack: Bool @Binding var canGoForward: Bool func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> WKWebView { let config = WKWebViewConfiguration() config.defaultWebpagePreferences.allowsContentJavaScript = true config.mediaTypesRequiringUserActionForPlayback = [] let webView = WKWebView(frame: .zero, configuration: config) webView.navigationDelegate = context.coordinator context.coordinator.webView = webView if let url = URL(string: urlString) { webView.load(URLRequest(url: url)) } let nc = NotificationCenter.default nc.addObserver(context.coordinator, selector: #selector(Coordinator.goBack), name: .videoViewGoBack, object: nil) nc.addObserver(context.coordinator, selector: #selector(Coordinator.goForward), name: .videoViewGoForward, object: nil) nc.addObserver(context.coordinator, selector: #selector(Coordinator.reload), name: .videoViewReload, object: nil) nc.addObserver(context.coordinator, selector: #selector(Coordinator.loadURL(_:)), name: .videoViewLoadURL, object: nil) webView.addObserver(context.coordinator, forKeyPath: #keyPath(WKWebView.canGoBack), options: .new, context: nil) webView.addObserver(context.coordinator, forKeyPath: #keyPath(WKWebView.canGoForward), options: .new, context: nil) webView.addObserver(context.coordinator, forKeyPath: #keyPath(WKWebView.isLoading), options: .new, context: nil) webView.addObserver(context.coordinator, forKeyPath: #keyPath(WKWebView.title), options: .new, context: nil) return webView } func updateNSView(_ nsView: WKWebView, context: Context) {} class Coordinator: NSObject, WKNavigationDelegate { var parent: VideoWebView weak var webView: WKWebView? init(_ parent: VideoWebView) { self.parent = parent } deinit { webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.canGoBack)) webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.canGoForward)) webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.isLoading)) webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.title)) NotificationCenter.default.removeObserver(self) } @objc func goBack() { webView?.goBack() } @objc func goForward() { webView?.goForward() } @objc func reload() { if webView?.isLoading == true { webView?.stopLoading() } else { webView?.reload() } } @objc func loadURL(_ notification: Notification) { if let urlStr = notification.object as? String, let url = URL(string: urlStr) { webView?.load(URLRequest(url: url)) } } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { guard let webView = webView else { return } DispatchQueue.main.async { self.parent.canGoBack = webView.canGoBack self.parent.canGoForward = webView.canGoForward self.parent.isLoading = webView.isLoading self.parent.currentTitle = webView.title ?? webView.url?.host ?? "Montana Video" } } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { DispatchQueue.main.async { self.parent.isLoading = false self.parent.currentTitle = webView.title ?? webView.url?.host ?? "Montana Video" } } } } // MARK: - Preview #Preview { VideoView() .environmentObject(PresenceEngine.shared) .frame(width: 600, height: 500) }