]> git.r.bdr.sh - rbdr/captura/commitdiff
Format the code
authorRuben Beltran del Rio <redacted>
Mon, 16 Sep 2024 09:07:46 +0000 (11:07 +0200)
committerRuben Beltran del Rio <redacted>
Mon, 16 Sep 2024 09:07:46 +0000 (11:07 +0200)
28 files changed:
Captura/CapturaApp.swift
Captura/Core Extensions/CGImage+resize.swift
Captura/Core Extensions/CVImageBuffer+cgImage.swift
Captura/Data/BackendResponse.swift
Captura/Data/CapturaFile.swift
Captura/Data/CapturaSettings.swift
Captura/Data/CapturaURLDecoder.swift
Captura/Data/GifRenderer.swift
Captura/Data/OutputFormatSetting.swift
Captura/Data/Persistence.swift
Captura/Domain/CapturaCaptureSession.swift
Captura/Domain/CaptureSessionConfiguration.swift
Captura/Intents/CapturaShortcutsProvider.swift
Captura/Intents/GetRemoteCaptures.swift
Captura/Presentation/Popovers/HelpPopoverViewController.swift
Captura/Presentation/Screens/PreferencesScreen.swift
Captura/Presentation/Settings/AboutSettings.swift
Captura/Presentation/Settings/AdvancedSettings.swift
Captura/Presentation/Settings/OutputSettings.swift
Captura/Presentation/Windows/PreferencesWindow.swift
Captura/Presentation/Windows/RecordingWindow.swift
Captura/Scripting/ConfigureCommand.swift
Captura/Scripting/RecordCommand.swift
Captura/Scripting/ScriptedPreferences.swift
CapturaTests/CapturaTests.swift
CapturaUITests/CapturaUITests.swift
CapturaUITests/CapturaUITestsLaunchTests.swift
Makefile

index dc358f7e71bf2e62b1626b9435a38fc3a8206fd1..5fe88584b7c48d74a06c79294353b477c9abc719 100644 (file)
@@ -1,3 +1,7 @@
+import AVFoundation
+import Cocoa
+import Combine
+import Sparkle
 /*
  Copyright (C) 2024 Rubén Beltrán del Río
 
  along with this program. If not, see https://captura.tranquil.systems.
  */
 import SwiftUI
-import Cocoa
-import Combine
-import AVFoundation
-import Sparkle
 
 @main
 struct CapturaApp: App {
 
-    @NSApplicationDelegateAdaptor(CapturaAppDelegate.self) var appDelegate
+  @NSApplicationDelegateAdaptor(CapturaAppDelegate.self) var appDelegate
 
-    var body: some Scene {
-        WindowGroup {
-          PreferencesScreen()
-              .handlesExternalEvents(preferring: Set(arrayLiteral: "PreferencesScreen"), allowing: Set(arrayLiteral: "*"))
-              .frame(width: 650, height: 450)
-        }
-        .handlesExternalEvents(matching: Set(arrayLiteral: "PreferencesScreen"))
-        //.modelContainer(for: CapturaRemoteFile.self)
-      }
+  var body: some Scene {
+    WindowGroup {
+      PreferencesScreen()
+        .handlesExternalEvents(
+          preferring: Set(arrayLiteral: "PreferencesScreen"), allowing: Set(arrayLiteral: "*")
+        )
+        .frame(width: 650, height: 450)
+    }
+    .handlesExternalEvents(matching: Set(arrayLiteral: "PreferencesScreen"))
+    //.modelContainer(for: CapturaRemoteFile.self)
+  }
 }
 
-@objc(CapturaAppDelegate) class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
-    
+@objc(CapturaAppDelegate) class CapturaAppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate
+{
+
   @Environment(\.openURL) var openURL
   var statusItem: NSStatusItem!
   var captureState: CaptureState = .idle
@@ -54,13 +57,14 @@ struct CapturaApp: App {
   var stopTimer: DispatchWorkItem?
   var remoteFiles: [CapturaRemoteFile] = []
   var captureSessionConfiguration: CaptureSessionConfiguration = CaptureSessionConfiguration()
-  
+
   // Sparkle Configuration
   @IBOutlet var checkForUpdatesMenuItem: NSMenuItem!
-  let updaterController: SPUStandardUpdaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
-  
+  let updaterController: SPUStandardUpdaterController = SPUStandardUpdaterController(
+    startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
+
   @objc dynamic var scriptedPreferences: ScriptedPreferences = ScriptedPreferences()
-  
+
   func applicationDidFinishLaunching(_ notification: Notification) {
     setupStatusBar()
     NotificationCenter.default.addObserver(
@@ -71,54 +75,67 @@ struct CapturaApp: App {
     closeWindow()
     fetchRemoteItems()
   }
-  
+
   // MARK: - Setup Functions
-  
-  
+
   private func setupStatusBar() {
     statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
-    
+
     if let button = statusItem.button {
       button.image = NSImage(named: "Idle")
     }
-    
+
     statusItem.isVisible = true
     statusItem.menu = NSMenu()
     statusItem.menu?.delegate = self
-    
+
     // Create the Popover
     popover = NSPopover()
     popover?.contentViewController = HelpPopoverViewController()
     popover?.behavior = .transient
-    
+
     setupMenu()
   }
-  
+
   private func setupMenu() {
-    
+
     statusItem.menu?.removeAllItems()
-    
-    statusItem.menu?.addItem(NSMenuItem(title: "Record", action: #selector(CapturaAppDelegate.onClickStartRecording), keyEquivalent: ""))
-    if (remoteFiles.count > 0) {
+
+    statusItem.menu?.addItem(
+      NSMenuItem(
+        title: "Record", action: #selector(CapturaAppDelegate.onClickStartRecording),
+        keyEquivalent: ""))
+    if remoteFiles.count > 0 {
       statusItem.menu?.addItem(NSMenuItem.separator())
       for remoteFile in remoteFiles {
-        let remoteFileItem = NSMenuItem(title: remoteFile.name, action: #selector(CapturaAppDelegate.onClickRemoteFile), keyEquivalent: "")
+        let remoteFileItem = NSMenuItem(
+          title: remoteFile.name, action: #selector(CapturaAppDelegate.onClickRemoteFile),
+          keyEquivalent: "")
         remoteFileItem.representedObject = remoteFile
         statusItem.menu?.addItem(remoteFileItem)
       }
     }
     statusItem.menu?.addItem(NSMenuItem.separator())
-    statusItem.menu?.addItem(NSMenuItem(title: "Open Local Folder", action: #selector(CapturaAppDelegate.onOpenFolder), keyEquivalent: ""))
+    statusItem.menu?.addItem(
+      NSMenuItem(
+        title: "Open Local Folder", action: #selector(CapturaAppDelegate.onOpenFolder),
+        keyEquivalent: ""))
     statusItem.menu?.addItem(NSMenuItem.separator())
-    
-    checkForUpdatesMenuItem = NSMenuItem(title: "Check for Updates", action: #selector(SPUStandardUpdaterController.checkForUpdates(_:)), keyEquivalent: "")
+
+    checkForUpdatesMenuItem = NSMenuItem(
+      title: "Check for Updates",
+      action: #selector(SPUStandardUpdaterController.checkForUpdates(_:)), keyEquivalent: "")
     checkForUpdatesMenuItem.target = updaterController
     statusItem.menu?.addItem(checkForUpdatesMenuItem)
-    
-    statusItem.menu?.addItem(NSMenuItem(title: "Preferences", action: #selector(CapturaAppDelegate.onOpenPreferences), keyEquivalent: ""))
-    statusItem.menu?.addItem(NSMenuItem(title: "Quit", action: #selector(CapturaAppDelegate.onQuit), keyEquivalent: ""))
+
+    statusItem.menu?.addItem(
+      NSMenuItem(
+        title: "Preferences", action: #selector(CapturaAppDelegate.onOpenPreferences),
+        keyEquivalent: ""))
+    statusItem.menu?.addItem(
+      NSMenuItem(title: "Quit", action: #selector(CapturaAppDelegate.onQuit), keyEquivalent: ""))
   }
-  
+
   private func closeWindow() {
     if let window = NSApplication.shared.windows.first {
       window.close()
@@ -126,20 +143,24 @@ struct CapturaApp: App {
   }
 
   // MARK: - URL Event Handler
-  
+
   func application(_ application: NSApplication, open urls: [URL]) {
-    if (CapturaSettings.shouldAllowURLAutomation) {
+    if CapturaSettings.shouldAllowURLAutomation {
       for url in urls {
         if let action = CapturaURLDecoder.decodeParams(url: url) {
           switch action {
-            case let .configure(config):
-            NotificationCenter.default.post(name: .setConfiguration, object: nil, userInfo: [
-              "config": config
-            ])
-            case let .record(config):
-            NotificationCenter.default.post(name: .setCaptureSessionConfiguration, object: nil, userInfo: [
-              "config": config
-            ])
+          case let .configure(config):
+            NotificationCenter.default.post(
+              name: .setConfiguration, object: nil,
+              userInfo: [
+                "config": config
+              ])
+          case let .record(config):
+            NotificationCenter.default.post(
+              name: .setCaptureSessionConfiguration, object: nil,
+              userInfo: [
+                "config": config
+              ])
             NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
           }
         }
@@ -147,15 +168,16 @@ struct CapturaApp: App {
     } else {
       let alert = NSAlert()
       alert.messageText = "URL Automation Prevented"
-              alert.informativeText = "A website or application attempted to record your screen using URL Automation. If you want to allow this, enable it in Preferences."
-              alert.alertStyle = .warning
-              alert.addButton(withTitle: "OK")
-              alert.runModal()
+      alert.informativeText =
+        "A website or application attempted to record your screen using URL Automation. If you want to allow this, enable it in Preferences."
+      alert.alertStyle = .warning
+      alert.addButton(withTitle: "OK")
+      alert.runModal()
     }
   }
-  
+
   // MARK: - UI Event Handlers
-  
+
   func menuWillOpen(_ menu: NSMenu) {
     if captureState != .idle {
       menu.cancelTrackingWithoutAnimation()
@@ -169,27 +191,29 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   @objc private func onClickStartRecording() {
     NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
   }
-  
+
   @objc private func onOpenPreferences() {
     NSApp.activate(ignoringOtherApps: true)
     if preferencesWindow == nil {
-      preferencesWindow =  PreferencesWindow()
+      preferencesWindow = PreferencesWindow()
     } else {
       preferencesWindow?.makeKeyAndOrderFront(nil)
       preferencesWindow?.orderFrontRegardless()
     }
   }
-  
+
   @objc private func onOpenFolder() {
-    if let directory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first?.appendingPathComponent("captura") {
+    if let directory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first?
+      .appendingPathComponent("captura")
+    {
       NSWorkspace.shared.open(directory)
     }
   }
-  
+
   @objc private func onClickRemoteFile(_ sender: NSMenuItem) {
     if let remoteFile = sender.representedObject as? CapturaRemoteFile {
       if let urlString = remoteFile.url {
@@ -199,15 +223,15 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   @objc private func onQuit() {
     NSApplication.shared.terminate(self)
   }
-  
+
   // MARK: - App State Event Listeners
-  
+
   @objc func didReceiveNotification(_ notification: Notification) {
-    switch(notification.name) {
+    switch notification.name {
     case .startAreaSelection:
       startAreaSelection()
     case .startRecording:
@@ -241,7 +265,7 @@ struct CapturaApp: App {
         }
       }
     case .reloadConfiguration:
-        reloadConfiguration()
+      reloadConfiguration()
     case .setCaptureSessionConfiguration:
       if let userInfo = notification.userInfo {
         if let config = userInfo["config"] as? RecordAction {
@@ -257,8 +281,7 @@ struct CapturaApp: App {
       return
     }
   }
-  
-  
+
   func startAreaSelection() {
     helpShown = false
     if captureState != .selectingArea {
@@ -285,25 +308,26 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   func startRecording() {
     captureState = .recording
     updateImage()
     outputFile = nil
-    images = [];
+    images = []
     pixelDensity = recordingWindow?.pixelDensity ?? 1.0
     recordingWindow?.recordingContentView.startRecording()
     if let box = recordingWindow?.recordingContentView.box {
       if let screen = recordingWindow?.screen {
         captureSession = CapturaCaptureSession(screen, box: box)
-        
+
         if let captureSession {
 
           stopTimer = DispatchWorkItem {
             self.stopRecording()
           }
-          DispatchQueue.main.asyncAfter(deadline: .now() + Double(captureSessionConfiguration.maxLength), execute: stopTimer!)
-          
+          DispatchQueue.main.asyncAfter(
+            deadline: .now() + Double(captureSessionConfiguration.maxLength), execute: stopTimer!)
+
           outputFile = CapturaFile()
           if captureSessionConfiguration.shouldSaveMp4 {
             captureSession.startRecording(to: outputFile!.mp4URL)
@@ -316,16 +340,17 @@ struct CapturaApp: App {
     }
     NotificationCenter.default.post(name: .failedToStart, object: nil, userInfo: nil)
   }
-  
+
   func stopRecording() {
     captureState = .uploading
     updateImage()
     stop()
-    
+
     Task.detached {
       if self.captureSessionConfiguration.shouldSaveGif {
         if let outputFile = self.outputFile {
-          await GifRenderer.render(self.images, at: self.captureSessionConfiguration.frameRate, to: outputFile.gifURL)
+          await GifRenderer.render(
+            self.images, at: self.captureSessionConfiguration.frameRate, to: outputFile.gifURL)
         }
       }
       let wasSuccessful = await self.uploadOrCopy()
@@ -336,7 +361,7 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   func finalizeRecording() {
     captureState = .uploaded
     updateImage()
@@ -344,18 +369,20 @@ struct CapturaApp: App {
       NotificationCenter.default.post(name: .reset, object: nil, userInfo: nil)
     }
   }
-  
+
   func reset() {
     captureState = .idle
     updateImage()
     captureSessionConfiguration = CaptureSessionConfiguration()
     stop()
   }
-  
+
   func receivedFrame(_ frame: CVImageBuffer) {
     let now = ContinuousClock.now
-    
-    if now - gifCallbackTimer > .nanoseconds(1_000_000_000 / UInt64(captureSessionConfiguration.frameRate)) {
+
+    if now - gifCallbackTimer
+      > .nanoseconds(1_000_000_000 / UInt64(captureSessionConfiguration.frameRate))
+    {
       gifCallbackTimer = now
       DispatchQueue.main.async {
         if var cgImage = frame.cgImage {
@@ -367,7 +394,7 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   func failed(_ requestPermission: Bool = false) {
     captureState = .error
     updateImage()
@@ -379,41 +406,42 @@ struct CapturaApp: App {
       NotificationCenter.default.post(name: .reset, object: nil, userInfo: nil)
     }
   }
-  
+
   func setConfiguration(_ config: ConfigureAction) {
     CapturaSettings.apply(config)
   }
-  
+
   func reloadConfiguration() {
     self.captureSessionConfiguration = CaptureSessionConfiguration()
   }
-  
+
   func setCaptureSessionConfiguration(_ config: RecordAction) {
     self.captureSessionConfiguration = CaptureSessionConfiguration(from: config)
   }
-  
+
   // MARK: - CoreData
-  
+
   private func fetchRemoteItems() {
     let viewContext = PersistenceController.shared.container.viewContext
     let fetchRequest = NSFetchRequest<CapturaRemoteFile>(entityName: "CapturaRemoteFile")
     fetchRequest.fetchLimit = 5
     fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)]
-    
+
     let results = try? viewContext.fetch(fetchRequest)
     remoteFiles = results ?? []
   }
-  
+
   // MARK: - Presentation Helpers
-  
-  
+
   private func requestPermissionToRecord() {
     showPopoverWithMessage("Please grant Captura permission to record")
-    if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenRecording") {
+    if let url = URL(
+      string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenRecording")
+    {
       NSWorkspace.shared.open(url)
     }
   }
-  
+
   private func showPopoverWithMessage(_ message: String) {
     if let button = statusItem.button {
       (self.popover?.contentViewController as? HelpPopoverViewController)?.updateLabel(message)
@@ -423,31 +451,32 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   private func updateImage() {
     if let button = statusItem.button {
-      let image: String = switch captureState {
-      case .idle:
-        "Idle"
-      case .selectingArea:
-        if recordingWindow?.recordingContentView.box != nil {
-          "Ready to Record"
-        } else {
-          "Selecting"
+      let image: String =
+        switch captureState {
+        case .idle:
+          "Idle"
+        case .selectingArea:
+          if recordingWindow?.recordingContentView.box != nil {
+            "Ready to Record"
+          } else {
+            "Selecting"
+          }
+        case .recording:
+          "Stop Frame 1"
+        case .uploading:
+          "Upload Frame 1"
+        case .uploaded:
+          "OK"
+        case .error:
+          "ERR"
         }
-      case .recording:
-        "Stop Frame 1"
-      case .uploading:
-        "Upload Frame 1"
-      case .uploaded:
-        "OK"
-      case .error:
-        "ERR"
-      }
       button.image = NSImage(named: image)
     }
   }
-  
+
   private func stop() {
     stopTimer?.cancel()
     captureSession?.stopRunning()
@@ -456,7 +485,7 @@ struct CapturaApp: App {
     recordingWindow?.close()
     recordingWindow = nil
   }
-  
+
   private func uploadOrCopy() async -> Bool {
     if captureSessionConfiguration.shouldUseBackend {
       let result = await uploadToBackend()
@@ -469,10 +498,12 @@ struct CapturaApp: App {
       return true
     }
   }
-  
+
   private func copyLocalToClipboard() {
-    let fileType: NSPasteboard.PasteboardType = .init(rawValue: captureSessionConfiguration.shouldSaveGif ? "com.compuserve.gif" : "public.mpeg-4")
-    if let url = captureSessionConfiguration.shouldSaveGif ? outputFile?.gifURL : outputFile?.mp4URL {
+    let fileType: NSPasteboard.PasteboardType = .init(
+      rawValue: captureSessionConfiguration.shouldSaveGif ? "com.compuserve.gif" : "public.mpeg-4")
+    if let url = captureSessionConfiguration.shouldSaveGif ? outputFile?.gifURL : outputFile?.mp4URL
+    {
       if let data = try? Data(contentsOf: url) {
         let pasteboard = NSPasteboard.general
         pasteboard.declareTypes([fileType], owner: nil)
@@ -480,10 +511,12 @@ struct CapturaApp: App {
       }
     }
   }
-  
+
   private func uploadToBackend() async -> Bool {
     let contentType = captureSessionConfiguration.shouldUploadGif ? "image/gif" : "video/mp4"
-    if let url = captureSessionConfiguration.shouldUploadGif ? outputFile?.gifURL : outputFile?.mp4URL {
+    if let url = captureSessionConfiguration.shouldUploadGif
+      ? outputFile?.gifURL : outputFile?.mp4URL
+    {
       if let data = try? Data(contentsOf: url) {
         if let remoteUrl = captureSessionConfiguration.backend {
           var request = URLRequest(url: remoteUrl)
@@ -491,10 +524,10 @@ struct CapturaApp: App {
           request.httpBody = data
           request.setValue(contentType, forHTTPHeaderField: "Content-Type")
           request.setValue("Captura/1.0", forHTTPHeaderField: "User-Agent")
-          
+
           do {
             let (data, response) = try await URLSession.shared.data(for: request)
-            
+
             if let httpResponse = response as? HTTPURLResponse {
               if httpResponse.statusCode == 201 {
                 let answer = try JSONDecoder().decode(BackendResponse.self, from: data)
@@ -508,7 +541,7 @@ struct CapturaApp: App {
     }
     return false
   }
-  
+
   private func createRemoteFile(_ url: URL) {
     let viewContext = PersistenceController.shared.container.viewContext
     let remoteFile = CapturaRemoteFile(context: viewContext)
@@ -519,11 +552,11 @@ struct CapturaApp: App {
     pasteboard.declareTypes([.URL], owner: nil)
     pasteboard.setString(url.absoluteString, forType: .string)
   }
-  
+
   private func deleteLocalFiles() {
     if captureSessionConfiguration.shouldSaveGif {
       if let url = outputFile?.gifURL {
-          try? FileManager.default.removeItem(at: url)
+        try? FileManager.default.removeItem(at: url)
       }
     }
     if captureSessionConfiguration.shouldSaveMp4 {
index 02aaa6e69af91ede7e9ba2462020ed38e2bb0e38..9d1adaa047a881705c9a138aaeed7197a4227ae3 100644 (file)
@@ -20,18 +20,22 @@ extension CGImage {
   func resize(by scale: CGFloat) -> CGImage? {
     let width = Int(CGFloat(self.width) / scale)
     let height = Int(CGFloat(self.height) / scale)
-    
+
     let bitsPerComponent = self.bitsPerComponent
     let colorSpace = self.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!
     let bitmapInfo = self.bitmapInfo.rawValue
-    
-    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo) else {
-        return nil
+
+    guard
+      let context = CGContext(
+        data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: 0,
+        space: colorSpace, bitmapInfo: bitmapInfo)
+    else {
+      return nil
     }
-    
+
     context.interpolationQuality = .high
     context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))
-    
+
     return context.makeImage()
   }
 }
index eb62e7aefeffd26ff7ee75e767d2c94c687443cf..ba98855dbaca54c9ef77621b43bb7b5d05a91022 100644 (file)
@@ -18,19 +18,20 @@ import Foundation
 import ReplayKit
 
 extension CVImageBuffer {
-  
+
   private static let contextQueue = DispatchQueue(label: "com.example.contextQueue")
   static let sharedContext: CIContext = {
-          return CIContext()
+    return CIContext()
   }()
-  
+
   var cgImage: CGImage? {
     var result: CGImage?
     CVImageBuffer.contextQueue.sync {
       let ciImage = CIImage(cvImageBuffer: self)
       let width = CVPixelBufferGetWidth(self)
       let height = CVPixelBufferGetHeight(self)
-      result = CVImageBuffer.sharedContext.createCGImage(ciImage, from: CGRect(x: 0, y: 0, width: width, height: height))
+      result = CVImageBuffer.sharedContext.createCGImage(
+        ciImage, from: CGRect(x: 0, y: 0, width: width, height: height))
     }
     return result
   }
index f4316de54a3dd0dc2e87a8209031ddaae35308e8..92636f04193e5682556e6164bdcb0529368f34b4 100644 (file)
@@ -15,6 +15,7 @@
  along with this program. If not, see https://captura.tranquil.systems.
  */
 import Foundation
+
 struct BackendResponse: Decodable {
-    let url: URL
+  let url: URL
 }
index 798c5100d735fc225b70841950f373004ff726c8..125e90426063cf10ecaeeef46044c91ad60e3c65 100644 (file)
 import Foundation
 
 struct CapturaFile {
-  
+
   let name: String
   let baseDirectory: URL
   let appDirectory: String = "captura"
-  
+
   private var baseURL: URL {
     baseDirectory.appendingPathComponent("\(appDirectory)/\(name)")
   }
-  
+
   var mp4URL: URL {
     return baseURL.appendingPathExtension("mp4")
   }
-  
+
   var gifURL: URL {
     return baseURL.appendingPathExtension("gif")
   }
-  
+
   init() {
     let dateFormatter = DateFormatter()
     dateFormatter.dateStyle = .medium
     dateFormatter.timeStyle = .medium
     dateFormatter.locale = Locale.current
     let dateString = dateFormatter.string(from: Date()).replacingOccurrences(of: ":", with: ".")
-    
+
     self.name = "Captura \(dateString)"
-    self.baseDirectory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask).first!
-    try? FileManager.default.createDirectory(at: self.baseDirectory.appendingPathComponent(appDirectory),
-                                            withIntermediateDirectories: true,
-                                            attributes: nil)
+    self.baseDirectory = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)
+      .first!
+    try? FileManager.default.createDirectory(
+      at: self.baseDirectory.appendingPathComponent(appDirectory),
+      withIntermediateDirectories: true,
+      attributes: nil)
   }
 }
index c8af096b40e37f96367921e7271a4fe965989bc9..bfaa1ced0ebd9bdd30aa59af1d78c03924312835 100644 (file)
@@ -29,7 +29,7 @@ struct CapturaSettings {
       UserDefaults.standard.setValue(newValue, forKey: "frameRate")
     }
   }
-  
+
   static var outputFormats: OutputFormatSetting {
     get {
       OutputFormatSetting(rawValue: UserDefaults.standard.integer(forKey: "outputFormats")) ?? .all
@@ -38,27 +38,27 @@ struct CapturaSettings {
       UserDefaults.standard.setValue(newValue.rawValue, forKey: "outputFormats")
     }
   }
-  
+
   static var shouldSaveMp4: Bool {
     outputFormats.shouldSaveMp4() || (shouldUseBackend && shouldUploadMp4)
   }
-  
+
   static var shouldSaveGif: Bool {
     outputFormats.shouldSaveGif() || (shouldUseBackend && shouldUploadGif)
   }
-  
+
   static var shouldUploadGif: Bool {
     backendFormat.shouldSaveGif()
   }
-  
+
   static var shouldUploadMp4: Bool {
     backendFormat.shouldSaveMp4()
   }
-  
+
   static var shouldUseBackend: Bool {
     backend != nil
   }
-  
+
   static var backend: URL? {
     get {
       if let url = UserDefaults.standard.string(forKey: "backendUrl") {
@@ -70,16 +70,17 @@ struct CapturaSettings {
       UserDefaults.standard.setValue(newValue?.absoluteString, forKey: "backendUrl")
     }
   }
-  
+
   static var backendFormat: OutputFormatSetting {
     get {
-      OutputFormatSetting(rawValue: UserDefaults.standard.integer(forKey: "backendFormat")) ?? .gifOnly
+      OutputFormatSetting(rawValue: UserDefaults.standard.integer(forKey: "backendFormat"))
+        ?? .gifOnly
     }
     set {
       UserDefaults.standard.setValue(newValue.rawValue, forKey: "backendFormat")
     }
   }
-  
+
   static var shouldKeepLocalFiles: Bool {
     get {
       if UserDefaults.standard.object(forKey: "keepFiles") == nil {
@@ -92,7 +93,7 @@ struct CapturaSettings {
       UserDefaults.standard.set(newValue, forKey: "keepFiles")
     }
   }
-  
+
   static var shouldAllowURLAutomation: Bool {
     get {
       UserDefaults.standard.bool(forKey: "allowURLAutomation")
@@ -101,7 +102,7 @@ struct CapturaSettings {
       UserDefaults.standard.setValue(newValue, forKey: "allowURLAutomation")
     }
   }
-  
+
   static func apply(_ config: ConfigureAction) {
     if let fps = config.fps {
       frameRate = fps
index cbb8a055c027d2ccf6f197e82e1733c26ac81a31..a325dbe3ae036646261ecd855da14eb99923d36d 100644 (file)
@@ -27,7 +27,7 @@ protocol ConfigureActionProtocol {
 
 protocol RecordActionProtocol {
   var action: String { get }
-  
+
   var x: Int? { get }
   var y: Int? { get }
   var width: Int? { get }
@@ -73,45 +73,47 @@ struct RecordAction: RecordActionProtocol {
 }
 
 enum CapturaAction {
-    case record(RecordAction)
-    case configure(ConfigureAction)
+  case record(RecordAction)
+  case configure(ConfigureAction)
 }
 
 struct CapturaURLDecoder {
-  
+
   static func decodeParams(url: URL) -> CapturaAction? {
-      guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
-            let params = components.queryItems else {
-          return nil
-      }
+    guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
+      let params = components.queryItems
+    else {
+      return nil
+    }
+
+    var paramsDict = [String: Any]()
+
+    params.forEach { item in
+      paramsDict[item.name] = item.value
+    }
+
+    guard let action = paramsDict["action"] as? String else {
+      return nil
+    }
 
-      var paramsDict = [String: Any]()
+    switch action {
+    case "configure":
+      var fps = Int(paramsDict["fps"] as? String ?? "")
+      let backend = URL(string: paramsDict["backend"] as? String ?? "")
+      let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "")
+      let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "")
+      var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "")
 
-      params.forEach { item in
-          paramsDict[item.name] = item.value
+      if fps != nil {
+        fps = min(10, max(4, fps!))
       }
 
-      guard let action = paramsDict["action"] as? String else {
-          return nil
+      if backendOutput == .all {
+        backendOutput = .gifOnly
       }
 
-      switch action {
-      case "configure":
-        var fps = Int(paramsDict["fps"] as? String ?? "")
-        let backend = URL(string: paramsDict["backend"] as? String ?? "")
-        let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "")
-        let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "")
-        var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "")
-        
-        if fps != nil {
-          fps = min(10, max(4, fps!))
-        }
-        
-        if backendOutput == .all {
-          backendOutput = .gifOnly
-        }
-        
-        return .configure(ConfigureAction(
+      return .configure(
+        ConfigureAction(
           action: action,
           fps: fps,
           outputs: outputs,
@@ -120,41 +122,42 @@ struct CapturaURLDecoder {
           keepLocalFiles: keepLocalFiles
         ))
 
-      case "record":
-        let x = Int(paramsDict["x"] as? String ?? "")
-        let y = Int(paramsDict["y"] as? String ?? "")
-        let width = Int(paramsDict["width"] as? String ?? "")
-        let height = Int(paramsDict["height"] as? String ?? "")
-        let preventResize = Bool(paramsDict["prevent_resize"] as? String ?? "")
-        let preventMove = Bool(paramsDict["prevent_move"] as? String ?? "")
-        var fps = Int(paramsDict["fps"] as? String ?? "")
-        let backend = URL(string: paramsDict["backend"] as? String ?? "")
-        let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "")
-        let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "")
-        var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "")
-        let autoStart = Bool(paramsDict["auto_start"] as? String ?? "")
-        var maxLength = Int(paramsDict["max_length"] as? String ?? "")
-        
-        if fps != nil {
-          fps = min(10, max(4, fps!))
-        }
-        
-        if maxLength != nil {
-          maxLength = min(300, max(1, fps!))
-        }
-        
-        if backendOutput == .all {
-          backendOutput = .gifOnly
-        }
-        
-        var skipBackend = false
-        if let backendString = paramsDict["backend"] as? String {
-          if backendString == "" {
-            skipBackend = true
-          }
+    case "record":
+      let x = Int(paramsDict["x"] as? String ?? "")
+      let y = Int(paramsDict["y"] as? String ?? "")
+      let width = Int(paramsDict["width"] as? String ?? "")
+      let height = Int(paramsDict["height"] as? String ?? "")
+      let preventResize = Bool(paramsDict["prevent_resize"] as? String ?? "")
+      let preventMove = Bool(paramsDict["prevent_move"] as? String ?? "")
+      var fps = Int(paramsDict["fps"] as? String ?? "")
+      let backend = URL(string: paramsDict["backend"] as? String ?? "")
+      let keepLocalFiles = Bool(paramsDict["keep_local_files"] as? String ?? "")
+      let outputs = OutputFormatSetting(paramsDict["outputs"] as? String ?? "")
+      var backendOutput = OutputFormatSetting(paramsDict["backend_output"] as? String ?? "")
+      let autoStart = Bool(paramsDict["auto_start"] as? String ?? "")
+      var maxLength = Int(paramsDict["max_length"] as? String ?? "")
+
+      if fps != nil {
+        fps = min(10, max(4, fps!))
+      }
+
+      if maxLength != nil {
+        maxLength = min(300, max(1, fps!))
+      }
+
+      if backendOutput == .all {
+        backendOutput = .gifOnly
+      }
+
+      var skipBackend = false
+      if let backendString = paramsDict["backend"] as? String {
+        if backendString == "" {
+          skipBackend = true
         }
-        
-        return .record(RecordAction(
+      }
+
+      return .record(
+        RecordAction(
           action: action,
           x: x,
           y: y,
@@ -172,8 +175,8 @@ struct CapturaURLDecoder {
           maxLength: maxLength
         ))
 
-      default:
-          return nil
-      }
+    default:
+      return nil
+    }
   }
 }
index bfbb289eea82e4eeb1a1af6d8d2e39d8b17a6587..2435cb464f48f4fb038f17243dbc26d704ec3c52 100644 (file)
@@ -1,3 +1,5 @@
+import CoreGraphics
+import SwiftUI
 /*
  Copyright (C) 2024 Rubén Beltrán del Río
 
  along with this program. If not, see https://captura.tranquil.systems.
  */
 import UniformTypeIdentifiers
-import SwiftUI
-import CoreGraphics
 
 struct GifRenderer {
   static func render(_ images: [CGImage], at fps: Int, to url: URL) async {
     let framedelay = String(format: "%.3f", 1.0 / Double(fps))
-    let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
-    let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFUnclampedDelayTime as String: framedelay]]
+    let fileProperties = [
+      kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]
+    ]
+    let gifProperties = [
+      kCGImagePropertyGIFDictionary as String: [
+        kCGImagePropertyGIFUnclampedDelayTime as String: framedelay
+      ]
+    ]
     let cfURL = url as CFURL
-    if let destination = CGImageDestinationCreateWithURL(cfURL, UTType.gif.identifier as CFString, images.count, nil) {
+    if let destination = CGImageDestinationCreateWithURL(
+      cfURL, UTType.gif.identifier as CFString, images.count, nil)
+    {
       CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
       for image in images {
         CGImageDestinationAddImage(destination, image, gifProperties as CFDictionary?)
index fd48f2fe8cabdaa71d6c7e364b365596204d402a..2131caa80659db9d0e019b9b1041eafad2a3f830 100644 (file)
@@ -20,9 +20,9 @@ enum OutputFormatSetting: Int {
   case gifOnly = 0
   case mp4Only = 1
   case all = 2
-  
+
   init?(_ string: String) {
-    switch(string) {
+    switch string {
     case "gif":
       self = .gifOnly
     case "mp4":
@@ -33,17 +33,17 @@ enum OutputFormatSetting: Int {
       return nil
     }
   }
-  
+
   func shouldSaveGif() -> Bool {
     return self == .gifOnly || self == .all
   }
-  
+
   func shouldSaveMp4() -> Bool {
     return self == .mp4Only || self == .all
   }
-  
+
   func toString() -> String {
-    switch(self) {
+    switch self {
     case .gifOnly:
       return "gif"
     case .mp4Only:
index 3ae66c351efddb623238540d4d7c1e6bd3c414b7..55dee5653723d36420339db6dc3ba6ee88ef4f33 100644 (file)
 import CoreData
 
 struct PersistenceController {
-    static let shared = PersistenceController()
+  static let shared = PersistenceController()
 
-    static var preview: PersistenceController = {
-        let result = PersistenceController(inMemory: true)
-        return result
-    }()
+  static var preview: PersistenceController = {
+    let result = PersistenceController(inMemory: true)
+    return result
+  }()
 
-    let container: NSPersistentCloudKitContainer
+  let container: NSPersistentCloudKitContainer
+
+  init(inMemory: Bool = false) {
+    container = NSPersistentCloudKitContainer(name: "Captura")
 
-    init(inMemory: Bool = false) {
-      container = NSPersistentCloudKitContainer(name: "Captura")
-      
     #if DEBUG
-    do {
+      do {
         // Use the container to initialize the development schema.
         try container.initializeCloudKitSchema(options: [])
-    } catch {
+      } catch {
         // Handle any errors.
-    }
+      }
     #endif
-      
-        if inMemory {
-            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
-        }
-        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
-            if let error = error as NSError? {
-                // Replace this implementation with code to handle the error appropriately.
-                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
-
-                /*
+
+    if inMemory {
+      container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
+    }
+    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
+      if let error = error as NSError? {
+        // Replace this implementation with code to handle the error appropriately.
+        // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
+
+        /*
                  Typical reasons for an error here include:
                  * The parent directory does not exist, cannot be created, or disallows writing.
                  * The persistent store is not accessible, due to permissions or data protection when the device is locked.
@@ -54,9 +54,9 @@ struct PersistenceController {
                  * The store could not be migrated to the current model version.
                  Check the error message to determine what the actual problem was.
                  */
-                fatalError("Unresolved error \(error), \(error.userInfo)")
-            }
-        })
-        container.viewContext.automaticallyMergesChangesFromParent = true
-    }
+        fatalError("Unresolved error \(error), \(error.userInfo)")
+      }
+    })
+    container.viewContext.automaticallyMergesChangesFromParent = true
+  }
 }
index 89432822ce340547bad0c7774a00f5107502a426..dbc72b9bccef479d46e69dfa2d22218893408ee4 100644 (file)
@@ -1,64 +1,75 @@
-import AppKit
 import AVFoundation
+import AppKit
 
-class CapturaCaptureSession: AVCaptureSession, AVCaptureFileOutputRecordingDelegate, AVCaptureVideoDataOutputSampleBufferDelegate {
+class CapturaCaptureSession: AVCaptureSession, AVCaptureFileOutputRecordingDelegate,
+  AVCaptureVideoDataOutputSampleBufferDelegate
+{
 
   let videoOutput = AVCaptureVideoDataOutput()
   let movieFileOutput = AVCaptureMovieFileOutput()
   var receivedFrames = false
-  
+
   init(_ screen: NSScreen, box: NSRect) {
     super.init()
-    
-    let displayId = screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! CGDirectDisplayID
+
+    let displayId =
+      screen.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as! CGDirectDisplayID
     let screenInput = AVCaptureScreenInput(displayID: displayId)
     var croppingBox = NSOffsetRect(box, -screen.frame.origin.x, -screen.frame.origin.y)
     if croppingBox.width.truncatingRemainder(dividingBy: 2) != 0 {
       croppingBox.size.width -= 1
     }
     screenInput?.cropRect = croppingBox.insetBy(dx: 1, dy: 1)
-    
+
     if self.canAddInput(screenInput!) {
       self.addInput(screenInput!)
     }
-    
-    videoOutput.setSampleBufferDelegate(self, queue: Dispatch.DispatchQueue(label: "sample buffer delegate", attributes: []))
-    
+
+    videoOutput.setSampleBufferDelegate(
+      self, queue: Dispatch.DispatchQueue(label: "sample buffer delegate", attributes: []))
+
     if self.canAddOutput(videoOutput) {
       self.addOutput(videoOutput)
     }
-    
+
     if self.canAddOutput(movieFileOutput) {
       self.addOutput(movieFileOutput)
     }
   }
-  
+
   func startRecording() {
     receivedFrames = false
     self.startRunning()
-    
+
     DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
       if !self.receivedFrames {
         NotificationCenter.default.post(name: .failedToStart, object: nil, userInfo: nil)
       }
     }
   }
-  
+
   func startRecording(to url: URL) {
     self.startRecording()
     movieFileOutput.startRecording(to: url, recordingDelegate: self)
   }
-  
+
   // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate Implementation
-  
-  func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
+
+  func captureOutput(
+    _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer,
+    from connection: AVCaptureConnection
+  ) {
     receivedFrames = true
-    
+
     guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
-    NotificationCenter.default.post(name: .receivedFrame, object: nil, userInfo: ["frame": imageBuffer])
+    NotificationCenter.default.post(
+      name: .receivedFrame, object: nil, userInfo: ["frame": imageBuffer])
   }
-  
+
   // MARK: - AVCaptureFileOutputRecordingDelegate Implementation
-  
-  func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {}
+
+  func fileOutput(
+    _ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL,
+    from connections: [AVCaptureConnection], error: Error?
+  ) {}
 }
index c2ebe75d097b6568d6bf4e6e79adac68d34c78ae..673db5f563f39200a06b38f19954e752b6c71aac 100644 (file)
@@ -14,7 +14,7 @@ struct CaptureSessionConfiguration {
   let preventResize: Bool
   let autoStart: Bool
   let maxLength: Int
-  
+
   init(
     frameRate: Int? = nil,
     outputFormats: OutputFormatSetting? = nil,
@@ -36,7 +36,7 @@ struct CaptureSessionConfiguration {
     autoStart = false
     maxLength = 300
   }
-  
+
   init(from action: RecordAction) {
     self.frameRate = action.fps ?? CapturaSettings.frameRate
     self.outputFormats = action.outputs ?? CapturaSettings.outputFormats
@@ -56,23 +56,23 @@ struct CaptureSessionConfiguration {
     autoStart = action.autoStart ?? false
     maxLength = action.maxLength ?? 300
   }
-  
+
   var shouldSaveMp4: Bool {
     outputFormats.shouldSaveMp4() || (shouldUseBackend && shouldUploadMp4)
   }
-  
+
   var shouldSaveGif: Bool {
     outputFormats.shouldSaveGif() || (shouldUseBackend && shouldUploadGif)
   }
-  
+
   var shouldUploadGif: Bool {
     backendFormat.shouldSaveGif()
   }
-  
+
   var shouldUploadMp4: Bool {
     backendFormat.shouldSaveMp4()
   }
-  
+
   var shouldUseBackend: Bool {
     backend != nil
   }
index 9df2170bc4aed4df5ac7608c327a09b47f27ee61..8846f2cd7d98f4cb777cb440e2c0eb31d9811bdd 100644 (file)
@@ -18,9 +18,9 @@ import AppIntents
 
 struct CapturaShortcutsProvider: AppShortcutsProvider {
 
-    static var appShortcuts: [AppShortcut] {
+  static var appShortcuts: [AppShortcut] {
 
-        AppShortcut(intent: GetRemoteCaptures(), phrases: ["Get \(.applicationName) remote captures"])
+    AppShortcut(intent: GetRemoteCaptures(), phrases: ["Get \(.applicationName) remote captures"])
 
-    }
+  }
 }
index a20cd727d643c7e1ca10ceba25a76af2637c8dd8..b0668014a8995ce0da40b30bb2a7ac40a3907bf2 100644 (file)
@@ -18,28 +18,28 @@ import AppIntents
 import CoreData
 
 struct GetRemoteCaptures: AppIntent {
-    static var title: LocalizedStringResource = "Get remote captures"
-    
-    static var description =
-        IntentDescription("Return a list of remote captures")
-  
+  static var title: LocalizedStringResource = "Get remote captures"
+
+  static var description =
+    IntentDescription("Return a list of remote captures")
+
   @Parameter(title: "Count") var count: Int?
-  
+
   static var parameterSummary: some ParameterSummary {
-      Summary("Get \(\.$count) latest captures.")
+    Summary("Get \(\.$count) latest captures.")
   }
-  
+
   func perform() async throws -> some IntentResult & ReturnsValue {
     let viewContext = PersistenceController.shared.container.viewContext
     let fetchRequest = NSFetchRequest<CapturaRemoteFile>(entityName: "CapturaRemoteFile")
     fetchRequest.fetchLimit = min(10, max(1, count ?? 5))
     fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)]
-    
+
     let results = await viewContext.perform {
       return try? viewContext.fetch(fetchRequest)
     }
-    
-    let finalResults = results?.compactMap { URL(string: $0.url ?? "")} ?? []
+
+    let finalResults = results?.compactMap { URL(string: $0.url ?? "") } ?? []
     return .result(value: finalResults)
   }
 }
index 2c48c788de53aa253972b006e504a9572a5f2ac4..5db77316e51dae5b18f5b94688105ba0b435c5fd 100644 (file)
 import Cocoa
 
 class HelpPopoverViewController: NSViewController {
-  
-    var labelString: String = "Captura"
-    let textField = NSTextField()
-  
-    override func loadView() {
-      self.view = NSView()
-      self.view.frame = NSRect(x: 0, y: 0, width: 250, height: 40)
-
-      textField.stringValue = labelString
-      textField.font = NSFont(name: "Hiragino Mincho ProN", size: 12)
-      textField.isEditable = false
-      textField.isBezeled = false
-      textField.isSelectable = false
-      textField.backgroundColor = NSColor.clear
-      textField.sizeToFit()
-      
-      let x = (view.frame.width - textField.frame.width) / 2
-      let y = (view.frame.height - textField.frame.height) / 2
-      textField.frame.origin = NSPoint(x: x, y: y)
-
-      self.view.addSubview(textField)
-    }
-  
+
+  var labelString: String = "Captura"
+  let textField = NSTextField()
+
+  override func loadView() {
+    self.view = NSView()
+    self.view.frame = NSRect(x: 0, y: 0, width: 250, height: 40)
+
+    textField.stringValue = labelString
+    textField.font = NSFont(name: "Hiragino Mincho ProN", size: 12)
+    textField.isEditable = false
+    textField.isBezeled = false
+    textField.isSelectable = false
+    textField.backgroundColor = NSColor.clear
+    textField.sizeToFit()
+
+    let x = (view.frame.width - textField.frame.width) / 2
+    let y = (view.frame.height - textField.frame.height) / 2
+    textField.frame.origin = NSPoint(x: x, y: y)
+
+    self.view.addSubview(textField)
+  }
+
   func updateLabel(_ newLabel: String) {
     labelString = newLabel
     textField.stringValue = labelString
index 9d1b2e007978fa0e058ae8b1b48fafe182bc335f..e149af14cbb3415cdc15c5711d866bec16db8ba6 100644 (file)
 import SwiftUI
 
 struct PreferencesScreen: View {
-    var body: some View {
-      TabView {
-        OutputSettings().tabItem {
-          Label("Output", systemImage: "video.fill")
-        }.padding(8.0).frame(minWidth: 300, minHeight: 130)
-        AdvancedSettings().tabItem {
-          Label("Advanced", systemImage: "gear")
-        }.padding(8.0).frame(minWidth: 300, minHeight: 260)
-        AboutSettings().tabItem {
-          Label("About", systemImage: "questionmark.circle.fill")
-        }.padding(8.0).frame(minWidth: 300, minHeight: 260)
-      }.padding(16.0)
-    }
+  var body: some View {
+    TabView {
+      OutputSettings().tabItem {
+        Label("Output", systemImage: "video.fill")
+      }.padding(8.0).frame(minWidth: 300, minHeight: 130)
+      AdvancedSettings().tabItem {
+        Label("Advanced", systemImage: "gear")
+      }.padding(8.0).frame(minWidth: 300, minHeight: 260)
+      AboutSettings().tabItem {
+        Label("About", systemImage: "questionmark.circle.fill")
+      }.padding(8.0).frame(minWidth: 300, minHeight: 260)
+    }.padding(16.0)
+  }
 }
 
 #Preview {
index 089ae1d0213156623514a634a567a5cc7e791d71..529907c1e0aa75e2f297f5b4de1590071f309a8c 100644 (file)
 import SwiftUI
 
 struct AboutSettings: View {
-  
+
   var appVersion: String {
-      let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
-      let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
-      return "Version \(version) (\(build))"
+    let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
+    let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
+    return "Version \(version) (\(build))"
   }
-  
-  var imprint = (try? AttributedString(markdown: "Captura is open source. Help and more information available at [captura.tranquil.systems](https://captura.tranquil.systems)")) ?? ""
+
+  var imprint =
+    (try? AttributedString(
+      markdown:
+        "Captura is open source. Help and more information available at [captura.tranquil.systems](https://captura.tranquil.systems)"
+    )) ?? ""
 
   var body: some View {
     Form {
-      VStack (alignment: .center) {
+      VStack(alignment: .center) {
         Text("Captura").bold()
         Text(appVersion).foregroundStyle(.secondary)
         Spacer()
index 79b6bb127306a14bcb7f2fc1bbdbbeb0c2ea5c8a..e148f01e63e7c8d7f76bb920c3a25fb6ac616aca 100644 (file)
 import SwiftUI
 
 struct AdvancedSettings: View {
-  
+
   @AppStorage("backendUrl") var backendUrl: String = ""
   @AppStorage("backendFormat") var backendFormat: OutputFormatSetting = .gifOnly
   @AppStorage("keepFiles") var keepFiles = true
   @AppStorage("allowURLAutomation") var allowURLAutomation = false
   @State var showConfirmation = false
-  
-  private var anyState: String { "\(backendUrl), \(backendFormat), \(keepFiles), \(allowURLAutomation)" }
-  
+
+  private var anyState: String {
+    "\(backendUrl), \(backendFormat), \(keepFiles), \(allowURLAutomation)"
+  }
+
   var parsedBackendUrl: URL? {
     URL(string: backendUrl)
   }
-  
+
   var body: some View {
     Form {
-      VStack (alignment: .center) {
+      VStack(alignment: .center) {
         Section {
-          VStack (alignment: .center) {
+          VStack(alignment: .center) {
             LabeledContent("Backend URL") {
               TextField("", text: $backendUrl).font(.body)
             }.font(.headline)
-              .help("The Backend URL to use. If this is empty, no backend will be used and the options below won't have an effect.")
+              .help(
+                "The Backend URL to use. If this is empty, no backend will be used and the options below won't have an effect."
+              )
             Picker(selection: $backendFormat, label: Text("Backend Format").font(.headline)) {
               Text("GIF")
                 .tag(OutputFormatSetting.gifOnly)
@@ -51,15 +55,21 @@ struct AdvancedSettings: View {
             }
             .pickerStyle(.radioGroup)
             .disabled(parsedBackendUrl == nil)
-            .help("The format picked here will be generated regardless of what option you pick in the output settings. It doesn't prevent files from being rendered.")
+            .help(
+              "The format picked here will be generated regardless of what option you pick in the output settings. It doesn't prevent files from being rendered."
+            )
             Toggle("Keep Local Files", isOn: $keepFiles)
               .font(.headline)
               .disabled(parsedBackendUrl == nil)
               .padding(.vertical, 8.0)
-              .help("If this is off, locally generated recordings will be deleted immediately after a successful upload.")
+              .help(
+                "If this is off, locally generated recordings will be deleted immediately after a successful upload."
+              )
             HStack {
-              Text("These settings can break things! Please make sure you understand how to use them before enabling.")
-                .lineLimit(3...10)
+              Text(
+                "These settings can break things! Please make sure you understand how to use them before enabling."
+              )
+              .lineLimit(3...10)
               Link(destination: URL(string: "https://captura.tranquil.systems")!) {
                 Image(systemName: "info.circle")
               }.buttonStyle(.borderless)
@@ -71,21 +81,29 @@ struct AdvancedSettings: View {
           Toggle("Allow URL Based Automation", isOn: $allowURLAutomation)
             .font(.headline)
             .padding(.vertical, 8.0)
-            .help("If this is on, the app can be controlled remotely using the captura: URL scheme.")
-            .confirmationDialog("This may be dangerous and can allow websites to remotely record your computer.", isPresented: $showConfirmation, actions: {
-              Button("I Understand The Risk", role: .destructive) {
-                showConfirmation = false
-              }
-              Button("Cancel", role: .cancel) {
-                showConfirmation = false
-                allowURLAutomation = false
-              }
-            })
-            .onChange(of: allowURLAutomation, perform: { newValue in
-              if newValue {
-                showConfirmation = true
+            .help(
+              "If this is on, the app can be controlled remotely using the captura: URL scheme."
+            )
+            .confirmationDialog(
+              "This may be dangerous and can allow websites to remotely record your computer.",
+              isPresented: $showConfirmation,
+              actions: {
+                Button("I Understand The Risk", role: .destructive) {
+                  showConfirmation = false
+                }
+                Button("Cancel", role: .cancel) {
+                  showConfirmation = false
+                  allowURLAutomation = false
+                }
               }
-            })
+            )
+            .onChange(
+              of: allowURLAutomation,
+              perform: { newValue in
+                if newValue {
+                  showConfirmation = true
+                }
+              })
         }
         Spacer()
       }
index bfad7c70d7caecdc0baf818f3bcd48be154961e6..9dc5be96f05aaa06bd739530cbe0ba57445c0978 100644 (file)
 import SwiftUI
 
 struct OutputSettings: View {
-  
+
   @AppStorage("outputFormats") var outputFormats: OutputFormatSetting = .all
   @AppStorage("frameRate") var frameRate = 10.0
-  
+
   private var anyState: String { "\(outputFormats), \(frameRate)" }
-  
+
   var body: some View {
     Form {
-      VStack (alignment: .center) {
+      VStack(alignment: .center) {
         LabeledContent("GIF Framerate") {
-          Slider(value: $frameRate,  in: 4...10, step: 1) {
+          Slider(value: $frameRate, in: 4...10, step: 1) {
             Text("\(Int(frameRate))").font(.body).frame(width: 24)
           } minimumValueLabel: {
             Text("4")
@@ -44,7 +44,7 @@ struct OutputSettings: View {
             .tag(OutputFormatSetting.gifOnly)
             .padding(.horizontal, 4.0)
             .padding(.vertical, 2.0)
-          
+
           Text("Only MP4")
             .tag(OutputFormatSetting.mp4Only)
             .padding(.horizontal, 4.0)
index 95d479bf3b5c58712e02fe090eb8e371e6ccfadd..233d21ba393cc5cb72df99f645010722dd85aedf 100644 (file)
  along with this program. If not, see https://captura.tranquil.systems.
  */
 import Cocoa
-import SwiftUI
-
 import Foundation
+import SwiftUI
 
 class PreferencesWindow: NSWindow {
-  
+
   init() {
     super.init(
       contentRect: NSRect(x: 0, y: 0, width: 600, height: 600),
index a12c5fb0bbd83db33fdd11aeb38e4b1bc3032e2a..71e3a5577605ffd75735886b562b9f1518315ac9 100644 (file)
@@ -18,26 +18,25 @@ import Cocoa
 import Combine
 
 class RecordingWindow: NSWindow {
-  
+
   var pixelDensity: CGFloat {
     self.screen?.backingScaleFactor ?? 1.0
   }
-  
+
   var recordingContentView: RecordingContentView {
     self.contentView as! RecordingContentView
   }
-  
+
   init(_ configuration: CaptureSessionConfiguration, _ button: NSRect?) {
-    
+
     let boundingBox = NSScreen.screenWithMouse?.frame ?? NSZeroRect
-    
+
     super.init(
       contentRect: boundingBox,
       styleMask: [.borderless],
       backing: .buffered,
       defer: false)
 
-
     self.isReleasedWhenClosed = false
     self.collectionBehavior = [.canJoinAllSpaces]
     self.isMovableByWindowBackground = false
@@ -46,7 +45,8 @@ class RecordingWindow: NSWindow {
     self.titlebarAppearsTransparent = true
     self.setFrame(boundingBox, display: true)
     self.titleVisibility = .hidden
-    let recordingView = RecordingContentView(configuration, frame: boundingBox, button: button ?? NSZeroRect)
+    let recordingView = RecordingContentView(
+      configuration, frame: boundingBox, button: button ?? NSZeroRect)
     recordingView.frame = boundingBox
     self.contentView = recordingView
     self.backgroundColor = NSColor(white: 1.0, alpha: 0.001)
@@ -56,30 +56,30 @@ class RecordingWindow: NSWindow {
     self.isOpaque = false
     self.hasShadow = false
   }
-  
+
   // MARK: - Window Behavior Overrides
-  
+
   override func resetCursorRects() {
     super.resetCursorRects()
     let cursor = NSCursor.crosshair
     self.contentView?.addCursorRect(self.contentView!.bounds, cursor: cursor)
   }
-  
+
   override var canBecomeKey: Bool {
     return true
   }
-  
+
   override var canBecomeMain: Bool {
     return true
   }
-  
+
   override func resignMain() {
     super.resignMain()
     if (self.contentView as? RecordingContentView)?.state != .recording {
       self.ignoresMouseEvents = false
     }
   }
-  
+
   override func becomeMain() {
     super.becomeMain()
     if (self.contentView as? RecordingContentView)?.state != .recording {
@@ -89,11 +89,11 @@ class RecordingWindow: NSWindow {
 }
 
 enum RecordingWindowState {
-  case passthrough, idle, drawing, moving, resizing, recording;
+  case passthrough, idle, drawing, moving, resizing, recording
 }
 
 class RecordingContentView: NSView {
-  
+
   init(_ configuration: CaptureSessionConfiguration, frame: NSRect, button: NSRect) {
     self.buttonSize = button.size
     var buttonOffset = NSPoint()
@@ -109,11 +109,15 @@ class RecordingContentView: NSView {
     preventResize = configuration.preventResize
     preventMove = configuration.preventMove
     autoStart = configuration.autoStart
-    
+
     self.bounds = frame
-    self.button = NSRect(x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width, height: buttonSize.height)
-    
-    if configuration.x != nil || configuration.y != nil || configuration.width != nil || configuration.height != nil {
+    self.button = NSRect(
+      x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width,
+      height: buttonSize.height)
+
+    if configuration.x != nil || configuration.y != nil || configuration.width != nil
+      || configuration.height != nil
+    {
       box = NSRect(
         x: configuration.x ?? Int(frame.width / 2.0),
         y: configuration.y ?? Int(frame.height / 2.0),
@@ -121,18 +125,18 @@ class RecordingContentView: NSView {
         height: configuration.height ?? 400
       )
     }
-    
+
     if autoStart {
       DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
         NotificationCenter.default.post(name: .startRecording, object: nil, userInfo: nil)
       }
     }
   }
-  
+
   required init?(coder: NSCoder) {
     fatalError("init(coder:) has not been implemented")
   }
-  
+
   private let buttonSize: NSSize
   private let buttonOffset: NSPoint
   public var button: NSRect? = nil
@@ -144,68 +148,71 @@ class RecordingContentView: NSView {
   private var preventResize = false
   private var preventMove = false
   private var autoStart = false
-  
+
   private var resizeBox: NSRect? {
     if let box {
       return NSRect(x: box.maxX - 5, y: box.minY - 5, width: 10, height: 10)
     }
     return nil
   }
-  
+
   private var shouldPassthrough: Bool {
     state == .recording || state == .passthrough
   }
-  
+
   // MARK: - State changing API
-  
+
   public func startRecording() {
     state = .recording
     window?.ignoresMouseEvents = true
   }
-  
+
   public func stopRecording() {
-    
+
   }
-  
+
   public func reset() {
     state = .idle
     window?.ignoresMouseEvents = false
   }
-  
+
   public func startPassthrough() {
     state = .passthrough
     window?.ignoresMouseEvents = true
   }
-  
+
   public func stopPassthrough() {
     state = .idle
     window?.ignoresMouseEvents = false
   }
-  
+
   // MARK: - View Behavior Overrides
-  
+
   override func updateTrackingAreas() {
     super.updateTrackingAreas()
-    
+
     for trackingArea in self.trackingAreas {
       self.removeTrackingArea(trackingArea)
     }
 
-    let options: NSTrackingArea.Options = [.mouseEnteredAndExited, .activeInKeyWindow, .cursorUpdate, .mouseMoved]
-    let trackingArea = NSTrackingArea(rect: self.bounds, options: options, owner: self, userInfo: nil)
+    let options: NSTrackingArea.Options = [
+      .mouseEnteredAndExited, .activeInKeyWindow, .cursorUpdate, .mouseMoved,
+    ]
+    let trackingArea = NSTrackingArea(
+      rect: self.bounds, options: options, owner: self, userInfo: nil)
     self.addTrackingArea(trackingArea)
   }
-  
+
   override func mouseExited(with event: NSEvent) {
     if state == .idle && box == nil {
       self.moveWindow()
     }
   }
-  
+
   override func mouseMoved(with event: NSEvent) {
-    
+
     self.mouseLocation = self.convert(event.locationInWindow, from: nil)
-    
+
     if shouldPassthrough {
       NSCursor.arrow.set()
     } else {
@@ -231,7 +238,7 @@ class RecordingContentView: NSView {
 
     self.setNeedsDisplay(self.bounds)
   }
-  
+
   override func mouseDragged(with event: NSEvent) {
     self.mouseLocation = self.convert(event.locationInWindow, from: nil)
     if state == .drawing {
@@ -242,7 +249,7 @@ class RecordingContentView: NSView {
         height: round(abs(mouseLocation.y - origin.y))
       )
     }
-    
+
     if box != nil {
       if state == .moving {
         NSCursor.closedHand.set()
@@ -250,7 +257,7 @@ class RecordingContentView: NSView {
           x: self.boxOrigin.x - self.origin.x + self.mouseLocation.x,
           y: self.boxOrigin.y - self.origin.y + self.mouseLocation.y)
       }
-      
+
       if state == .resizing {
         box = NSRect(
           x: round(min(origin.x, mouseLocation.x)),
@@ -262,7 +269,7 @@ class RecordingContentView: NSView {
     }
     self.setNeedsDisplay(self.bounds)
   }
-  
+
   override func cursorUpdate(with event: NSEvent) {
     NSCursor.crosshair.set()
   }
@@ -270,7 +277,7 @@ class RecordingContentView: NSView {
   override func hitTest(_ point: NSPoint) -> NSView? {
     return shouldPassthrough ? nil : self
   }
-      
+
   override var acceptsFirstResponder: Bool {
     return true
   }
@@ -278,14 +285,14 @@ class RecordingContentView: NSView {
   override func mouseDown(with event: NSEvent) {
     self.origin = self.convert(event.locationInWindow, from: nil)
     if let box {
-      
+
       if let button {
         if button.contains(origin) {
           NotificationCenter.default.post(name: .startRecording, object: nil, userInfo: nil)
           return
         }
       }
-      
+
       if resizeBox!.contains(origin) && !preventResize {
         self.origin = NSPoint(x: box.minX, y: box.maxY)
         state = .resizing
@@ -297,11 +304,11 @@ class RecordingContentView: NSView {
         return
       }
     }
-    
+
     if preventResize || preventMove {
       return
     }
-    
+
     state = .drawing
   }
 
@@ -310,16 +317,16 @@ class RecordingContentView: NSView {
       state = .idle
     }
   }
-  
+
   override func keyDown(with event: NSEvent) {
     switch event.keyCode {
-      case 53:  // Escape key
-        NotificationCenter.default.post(name: .reset, object: nil, userInfo: nil)
-      default:
-        super.keyDown(with: event)
+    case 53:  // Escape key
+      NotificationCenter.default.post(name: .reset, object: nil, userInfo: nil)
+    default:
+      super.keyDown(with: event)
     }
   }
-  
+
   override func flagsChanged(with event: NSEvent) {
     if state == .idle {
       if event.modifierFlags.contains(.shift) {
@@ -329,7 +336,7 @@ class RecordingContentView: NSView {
       }
     }
   }
-  
+
   override func draw(_ dirtyRect: NSRect) {
     if shouldPassthrough {
       NSColor.clear.setFill()
@@ -337,54 +344,54 @@ class RecordingContentView: NSView {
       NSColor(white: 1.0, alpha: 0.001).setFill()
     }
     dirtyRect.fill()
-    
+
     let dashLength: CGFloat = 5.0
     let lineWidth = 0.5
 
-// Uncomment below to debug button placement visually
-//    if let button {
-//      let buttonPath = NSBezierPath()
-//      buttonPath.move(to: NSPoint(x: button.minX, y: button.minY))
-//      buttonPath.line(to: NSPoint(x: button.maxX, y: button.minY))
-//      buttonPath.line(to: NSPoint(x: button.maxX, y: button.maxY))
-//      buttonPath.line(to: NSPoint(x: button.minX, y: button.maxY))
-//      buttonPath.line(to: NSPoint(x: button.minX, y: button.minY))
-//      NSColor(red: 1, green: 0, blue: 1, alpha: 1).setFill()
-//      buttonPath.fill()
-//    }
+    // Uncomment below to debug button placement visually
+    //    if let button {
+    //      let buttonPath = NSBezierPath()
+    //      buttonPath.move(to: NSPoint(x: button.minX, y: button.minY))
+    //      buttonPath.line(to: NSPoint(x: button.maxX, y: button.minY))
+    //      buttonPath.line(to: NSPoint(x: button.maxX, y: button.maxY))
+    //      buttonPath.line(to: NSPoint(x: button.minX, y: button.maxY))
+    //      buttonPath.line(to: NSPoint(x: button.minX, y: button.minY))
+    //      NSColor(red: 1, green: 0, blue: 1, alpha: 1).setFill()
+    //      buttonPath.fill()
+    //    }
 
     if state == .idle && box == nil {
       let blackLine = NSBezierPath()
       blackLine.lineWidth = lineWidth
       blackLine.setLineDash([dashLength, dashLength], count: 2, phase: 0)
-      
+
       // Vertical line (Black)
       blackLine.move(to: NSPoint(x: self.mouseLocation.x, y: NSMinY(self.bounds)))
       blackLine.line(to: NSPoint(x: self.mouseLocation.x, y: NSMaxY(self.bounds)))
-      
+
       // Horizontal line (Black)
       blackLine.move(to: NSPoint(x: NSMinX(self.bounds), y: self.mouseLocation.y))
       blackLine.line(to: NSPoint(x: NSMaxX(self.bounds), y: self.mouseLocation.y))
-      
+
       NSColor.black.setStroke()
       blackLine.stroke()
-      
+
       let whiteLine = NSBezierPath()
       whiteLine.lineWidth = lineWidth
       whiteLine.setLineDash([dashLength, dashLength], count: 2, phase: dashLength)
-      
+
       // Vertical line (White)
       whiteLine.move(to: NSPoint(x: self.mouseLocation.x, y: NSMinY(self.bounds)))
       whiteLine.line(to: NSPoint(x: self.mouseLocation.x, y: NSMaxY(self.bounds)))
-      
+
       // Horizontal line (White)
       whiteLine.move(to: NSPoint(x: NSMinX(self.bounds), y: self.mouseLocation.y))
       whiteLine.line(to: NSPoint(x: NSMaxX(self.bounds), y: self.mouseLocation.y))
-      
+
       NSColor.white.setStroke()
       whiteLine.stroke()
     }
-    
+
     if let box {
       let blackBox = NSBezierPath()
       blackBox.lineWidth = lineWidth
@@ -396,7 +403,7 @@ class RecordingContentView: NSView {
       blackBox.line(to: NSPoint(x: box.minX, y: box.minY))
       NSColor.black.setStroke()
       blackBox.stroke()
-      
+
       let whiteBox = NSBezierPath()
       whiteBox.lineWidth = lineWidth
       whiteBox.setLineDash([dashLength, dashLength], count: 2, phase: dashLength)
@@ -407,11 +414,11 @@ class RecordingContentView: NSView {
       whiteBox.line(to: NSPoint(x: box.minX, y: box.minY))
       NSColor.white.setStroke()
       whiteBox.stroke()
-      
+
       if state == .recording {
         return
       }
-      
+
       if let resizeBox {
         let clearBox = NSBezierPath()
         clearBox.move(to: NSPoint(x: resizeBox.minX, y: resizeBox.minY))
@@ -422,46 +429,53 @@ class RecordingContentView: NSView {
         NSColor(white: 0, alpha: 0.2).setFill()
         clearBox.fill()
       }
-      
+
       if state == .moving {
         let string = "\(Int(box.minX)), \(Int(box.maxY))" as NSString
-        drawText(string, NSPoint(
-          x: box.minX,
-          y: box.maxY
-        ), true)
+        drawText(
+          string,
+          NSPoint(
+            x: box.minX,
+            y: box.maxY
+          ), true)
       }
-      
+
       if state == .resizing {
         let string = "\(Int(mouseLocation.x)), \(Int(mouseLocation.y))" as NSString
         drawText(string, mouseLocation)
       }
-      
+
       if box.contains(mouseLocation) && state != .resizing {
-        return;
+        return
       }
     }
-    
+
     // Draw text
 
     let string = "\(Int(mouseLocation.x)), \(Int(mouseLocation.y))" as NSString
     drawText(string, mouseLocation)
   }
-  
+
   // MARK: - Utilities
-  
+
   private func drawText(_ text: NSString, _ location: NSPoint, _ isBottomRight: Bool = false) {
-    
+
     let textAttributes = [
-      NSAttributedString.Key.font: NSFont(name: "Hiragino Mincho ProN", size: 12) ?? NSFont.systemFont(ofSize: 12),
+      NSAttributedString.Key.font: NSFont(name: "Hiragino Mincho ProN", size: 12)
+        ?? NSFont.systemFont(ofSize: 12),
       NSAttributedString.Key.foregroundColor: NSColor.white,
     ]
     let offset = NSPoint(x: 10, y: 10)
     let padding = NSPoint(x: 5, y: 2)
     let size = text.size(withAttributes: textAttributes)
-    var rect = NSRect(x: location.x + offset.x, y: location.y + offset.y, width: size.width + 2 * padding.x, height: size.height + 2 * padding.y)
-    var textRect = NSRect(x: location.x + offset.x + padding.x, y: location.y + offset.y + padding.y, width: size.width, height: size.height)
-    
-    if (isBottomRight) {
+    var rect = NSRect(
+      x: location.x + offset.x, y: location.y + offset.y, width: size.width + 2 * padding.x,
+      height: size.height + 2 * padding.y)
+    var textRect = NSRect(
+      x: location.x + offset.x + padding.x, y: location.y + offset.y + padding.y, width: size.width,
+      height: size.height)
+
+    if isBottomRight {
       rect = rect.offsetBy(dx: -size.width - 2 * offset.x, dy: 0)
       textRect = textRect.offsetBy(dx: -size.width - 2 * offset.x, dy: 0)
     }
@@ -471,7 +485,7 @@ class RecordingContentView: NSView {
 
     text.draw(in: textRect, withAttributes: textAttributes)
   }
-  
+
   private func moveWindow() {
     let screen = NSScreen.screenWithMouse
     if let currentScreen = self.window?.screen {
@@ -480,10 +494,12 @@ class RecordingContentView: NSView {
         self.frame = CGRect(origin: NSZeroPoint, size: frame.size)
         self.bounds = CGRect(origin: NSZeroPoint, size: frame.size)
         self.updateTrackingAreas()
-        
+
         if let window = self.window {
           self.bounds = frame
-          self.button = NSRect(x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width, height: buttonSize.height)
+          self.button = NSRect(
+            x: frame.maxX - buttonOffset.x, y: frame.maxY - buttonOffset.y, width: buttonSize.width,
+            height: buttonSize.height)
           window.setFrame(frame, display: true, animate: false)
           window.makeKeyAndOrderFront(nil)
           window.orderFrontRegardless()
index bb58c03d410a88ec53b3819f512d637598dc8f4d..28cfe36fe02af9a5934b10fac9766e0c72f21730 100644 (file)
@@ -18,32 +18,34 @@ import Foundation
 
 @objc(ConfigureCommand)
 class ConfigureCommand: NSScriptCommand {
-    override func performDefaultImplementation() -> Any? {
-      
-      let args = self.directParameter as? [String: Any] ?? [:]
-      
-      // Here you can extract the parameters from the args dictionary and configure your settings
-      let fps = args["fps"] as? Int
-      let outputs = OutputFormatSetting(args["outputs"] as? String ?? "")
-      let backend = URL(string: args["backend"] as? String ?? "")
-      let backendOutput = OutputFormatSetting(args["backend_output"] as? String ?? "")
-      let keepLocalFiles = args["keep_local_files"] as? Bool
-    
-      let config = ConfigureAction(
-        action: "configure",
-        fps: fps,
-        outputs: outputs,
-        backend: backend,
-        backendOutput: backendOutput,
-        keepLocalFiles: keepLocalFiles
-      )
-      
-      NotificationCenter.default.post(name: .setConfiguration, object: nil, userInfo: [
+  override func performDefaultImplementation() -> Any? {
+
+    let args = self.directParameter as? [String: Any] ?? [:]
+
+    // Here you can extract the parameters from the args dictionary and configure your settings
+    let fps = args["fps"] as? Int
+    let outputs = OutputFormatSetting(args["outputs"] as? String ?? "")
+    let backend = URL(string: args["backend"] as? String ?? "")
+    let backendOutput = OutputFormatSetting(args["backend_output"] as? String ?? "")
+    let keepLocalFiles = args["keep_local_files"] as? Bool
+
+    let config = ConfigureAction(
+      action: "configure",
+      fps: fps,
+      outputs: outputs,
+      backend: backend,
+      backendOutput: backendOutput,
+      keepLocalFiles: keepLocalFiles
+    )
+
+    NotificationCenter.default.post(
+      name: .setConfiguration, object: nil,
+      userInfo: [
         "config": config
       ])
-      NotificationCenter.default.post(name: .reloadConfiguration, object: nil, userInfo: nil)
+    NotificationCenter.default.post(name: .reloadConfiguration, object: nil, userInfo: nil)
 
-      // Return a result if needed
-      return nil
-    }
+    // Return a result if needed
+    return nil
+  }
 }
index 43cdd32070c62fe3730d3df661528ece44583d54..da2d28c466a3966fb0fb7226dd8299cbe87a9a3b 100644 (file)
@@ -18,56 +18,58 @@ import Foundation
 
 @objc(RecordCommand)
 class RecordCommand: NSScriptCommand {
-    override func performDefaultImplementation() -> Any? {
-      
-      let args = self.directParameter as? [String: Any] ?? [:]
-      
-      // Here you can extract the parameters from the args dictionary and configure your settings
-      let x = args["x"] as? Int
-      let y = args["y"] as? Int
-      let width = args["width"] as? Int
-      let height = args["height"] as? Int
-      let preventResize = args["prevent_resize"] as? Bool
-      let preventMove = args["prevent_move"] as? Bool
-      let fps = args["fps"] as? Int
-      let outputs = OutputFormatSetting(args["outputs"] as? String ?? "")
-      let backend = URL(string: args["backend"] as? String ?? "")
-      let backendOutput = OutputFormatSetting(args["backend_output"] as? String ?? "")
-      let keepLocalFiles = args["keep_local_files"] as? Bool
-      let autoStart = args["auto_start"] as? Bool
-      let maxLength = args["max_length"] as? Int
-      
-      var skipBackend = false
-      if let backendString = args["backend"] as? String {
-        if backendString == "" {
-          skipBackend = true
-        }
+  override func performDefaultImplementation() -> Any? {
+
+    let args = self.directParameter as? [String: Any] ?? [:]
+
+    // Here you can extract the parameters from the args dictionary and configure your settings
+    let x = args["x"] as? Int
+    let y = args["y"] as? Int
+    let width = args["width"] as? Int
+    let height = args["height"] as? Int
+    let preventResize = args["prevent_resize"] as? Bool
+    let preventMove = args["prevent_move"] as? Bool
+    let fps = args["fps"] as? Int
+    let outputs = OutputFormatSetting(args["outputs"] as? String ?? "")
+    let backend = URL(string: args["backend"] as? String ?? "")
+    let backendOutput = OutputFormatSetting(args["backend_output"] as? String ?? "")
+    let keepLocalFiles = args["keep_local_files"] as? Bool
+    let autoStart = args["auto_start"] as? Bool
+    let maxLength = args["max_length"] as? Int
+
+    var skipBackend = false
+    if let backendString = args["backend"] as? String {
+      if backendString == "" {
+        skipBackend = true
       }
-    
-      let config = RecordAction(
-        action: "record",
-        x: x,
-        y: y,
-        width: width,
-        height: height,
-        preventResize: preventResize,
-        preventMove: preventMove,
-        fps: fps,
-        outputs: outputs,
-        backend: backend,
-        backendOutput: backendOutput,
-        keepLocalFiles: keepLocalFiles,
-        autoStart: autoStart,
-        skipBackend: skipBackend,
-        maxLength: maxLength
-      )
-    
-      NotificationCenter.default.post(name: .setCaptureSessionConfiguration, object: nil, userInfo: [
+    }
+
+    let config = RecordAction(
+      action: "record",
+      x: x,
+      y: y,
+      width: width,
+      height: height,
+      preventResize: preventResize,
+      preventMove: preventMove,
+      fps: fps,
+      outputs: outputs,
+      backend: backend,
+      backendOutput: backendOutput,
+      keepLocalFiles: keepLocalFiles,
+      autoStart: autoStart,
+      skipBackend: skipBackend,
+      maxLength: maxLength
+    )
+
+    NotificationCenter.default.post(
+      name: .setCaptureSessionConfiguration, object: nil,
+      userInfo: [
         "config": config
       ])
-      NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
+    NotificationCenter.default.post(name: .startAreaSelection, object: nil, userInfo: nil)
 
-        // Return a result if needed
-        return nil
-    }
+    // Return a result if needed
+    return nil
+  }
 }
index 547c3cfe9a235051b3e2d7838e13c11f3540f2e4..72aa45ea2e34ed2d5ea159a9e28140bea7836e73 100644 (file)
@@ -35,7 +35,7 @@ class ScriptedPreferences: NSObject {
       CapturaSettings.outputFormats = OutputFormatSetting(newValue) ?? .gifOnly
     }
   }
-  
+
   @objc dynamic var backend: String {
     get {
       CapturaSettings.backend?.absoluteString ?? ""
@@ -44,7 +44,7 @@ class ScriptedPreferences: NSObject {
       CapturaSettings.backend = URL(string: newValue)
     }
   }
-  
+
   @objc dynamic var backend_output: String {
     get {
       CapturaSettings.backendFormat.toString()
@@ -53,7 +53,7 @@ class ScriptedPreferences: NSObject {
       CapturaSettings.backendFormat = OutputFormatSetting(newValue) ?? .gifOnly
     }
   }
-  
+
   @objc dynamic var keep_local_files: Bool {
     get {
       CapturaSettings.shouldKeepLocalFiles
index 363f42bd528135b655b59a3b10ef180b3157546e..26061af431f3467fa1adba02da79b0cf267032ba 100644 (file)
  */
 
 import XCTest
+
 @testable import Captura
 
 final class CapturaTests: XCTestCase {
 
-    override func setUpWithError() throws {
-        // Put setup code here. This method is called before the invocation of each test method in the class.
-    }
-
-    override func tearDownWithError() throws {
-        // Put teardown code here. This method is called after the invocation of each test method in the class.
-    }
-
-    func testExample() throws {
-        // This is an example of a functional test case.
-        // Use XCTAssert and related functions to verify your tests produce the correct results.
-        // Any test you write for XCTest can be annotated as throws and async.
-        // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
-        // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
-    }
-
-    func testPerformanceExample() throws {
-        // This is an example of a performance test case.
-        self.measure {
-            // Put the code you want to measure the time of here.
-        }
+  override func setUpWithError() throws {
+    // Put setup code here. This method is called before the invocation of each test method in the class.
+  }
+
+  override func tearDownWithError() throws {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+  }
+
+  func testExample() throws {
+    // This is an example of a functional test case.
+    // Use XCTAssert and related functions to verify your tests produce the correct results.
+    // Any test you write for XCTest can be annotated as throws and async.
+    // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
+    // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
+  }
+
+  func testPerformanceExample() throws {
+    // This is an example of a performance test case.
+    self.measure {
+      // Put the code you want to measure the time of here.
     }
+  }
 
 }
index 429c6b2e8a9b942b7a356dd7a82bc26e1f951ea8..f29b0e4fb53b423c44a0fbeba67ad64bf5f5b3b0 100644 (file)
@@ -19,33 +19,33 @@ import XCTest
 
 final class CapturaUITests: XCTestCase {
 
-    override func setUpWithError() throws {
-        // Put setup code here. This method is called before the invocation of each test method in the class.
+  override func setUpWithError() throws {
+    // Put setup code here. This method is called before the invocation of each test method in the class.
 
-        // In UI tests it is usually best to stop immediately when a failure occurs.
-        continueAfterFailure = false
+    // In UI tests it is usually best to stop immediately when a failure occurs.
+    continueAfterFailure = false
 
-        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
-    }
+    // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
+  }
 
-    override func tearDownWithError() throws {
-        // Put teardown code here. This method is called after the invocation of each test method in the class.
-    }
+  override func tearDownWithError() throws {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+  }
 
-    func testExample() throws {
-        // UI tests must launch the application that they test.
-        let app = XCUIApplication()
-        app.launch()
+  func testExample() throws {
+    // UI tests must launch the application that they test.
+    let app = XCUIApplication()
+    app.launch()
 
-        // Use XCTAssert and related functions to verify your tests produce the correct results.
-    }
+    // Use XCTAssert and related functions to verify your tests produce the correct results.
+  }
 
-    func testLaunchPerformance() throws {
-        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
-            // This measures how long it takes to launch your application.
-            measure(metrics: [XCTApplicationLaunchMetric()]) {
-                XCUIApplication().launch()
-            }
-        }
+  func testLaunchPerformance() throws {
+    if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
+      // This measures how long it takes to launch your application.
+      measure(metrics: [XCTApplicationLaunchMetric()]) {
+        XCUIApplication().launch()
+      }
     }
+  }
 }
index 98bab3118e8e25e4f7f463fb5cd9948083f24d07..a8d1b69535948186a5ecce92a62fec0a56fc08a3 100644 (file)
@@ -19,24 +19,24 @@ import XCTest
 
 final class CapturaUITestsLaunchTests: XCTestCase {
 
-    override class var runsForEachTargetApplicationUIConfiguration: Bool {
-        true
-    }
-
-    override func setUpWithError() throws {
-        continueAfterFailure = false
-    }
-
-    func testLaunch() throws {
-        let app = XCUIApplication()
-        app.launch()
-
-        // Insert steps here to perform after app launch but before taking a screenshot,
-        // such as logging into a test account or navigating somewhere in the app
-
-        let attachment = XCTAttachment(screenshot: app.screenshot())
-        attachment.name = "Launch Screen"
-        attachment.lifetime = .keepAlways
-        add(attachment)
-    }
+  override class var runsForEachTargetApplicationUIConfiguration: Bool {
+    true
+  }
+
+  override func setUpWithError() throws {
+    continueAfterFailure = false
+  }
+
+  func testLaunch() throws {
+    let app = XCUIApplication()
+    app.launch()
+
+    // Insert steps here to perform after app launch but before taking a screenshot,
+    // such as logging into a test account or navigating somewhere in the app
+
+    let attachment = XCTAttachment(screenshot: app.screenshot())
+    attachment.name = "Launch Screen"
+    attachment.lifetime = .keepAlways
+    add(attachment)
+  }
 }
index dd8bf56d69c56eaa52b5b89b994001ba2c4b24fc..f90daf664f89b84be327d89731b84ebe1fb6df0d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,4 +19,10 @@ archive: prepare
 prepare:
        mkdir -p $(build_directory)
 
-.PHONY: package prepare archive generate_appcast package distribute
+format:
+       swift format -i -r .
+
+lint:
+       swift format lint -r .
+
+.PHONY: package prepare archive generate_appcast package distribute format lint