Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
138fb4d417 |
@@ -9,6 +9,7 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15B60A72A2423760052F712 /* UserNotificationHandler.swift */; };
|
||||
C15F06A62A20198300C14CD8 /* Soyuz.help in Resources */ = {isa = PBXBuildFile; fileRef = C15F06A42A20171E00C14CD8 /* Soyuz.help */; };
|
||||
C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */; };
|
||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; };
|
||||
E16378B429A491E6002F05E9 /* MoonrakerSocketManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */; };
|
||||
E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* SoyuzApp.swift */; };
|
||||
@@ -47,6 +48,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
C15B60A72A2423760052F712 /* UserNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationHandler.swift; sourceTree = "<group>"; };
|
||||
C15F06A42A20171E00C14CD8 /* Soyuz.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Soyuz.help; sourceTree = "<group>"; };
|
||||
C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerNative.swift; sourceTree = "<group>"; };
|
||||
E124B9D72993FE5500C0D2D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterConfigView.swift; sourceTree = "<group>"; };
|
||||
E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerTests.swift; sourceTree = "<group>"; };
|
||||
@@ -175,6 +177,7 @@
|
||||
E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */,
|
||||
E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */,
|
||||
C15B60A72A2423760052F712 /* UserNotificationHandler.swift */,
|
||||
C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
@@ -318,6 +321,7 @@
|
||||
files = (
|
||||
E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */,
|
||||
E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */,
|
||||
C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */,
|
||||
E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */,
|
||||
E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */,
|
||||
C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */,
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
filePath = "SoyuzTests/MoonrakerSocketManagerTests.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "70"
|
||||
endingLineNumber = "70"
|
||||
startingLineNumber = "71"
|
||||
endingLineNumber = "71"
|
||||
landmarkName = "setUp()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -41,8 +41,8 @@
|
||||
filePath = "SoyuzTests/MoonrakerSocketManagerTests.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "72"
|
||||
endingLineNumber = "72"
|
||||
startingLineNumber = "73"
|
||||
endingLineNumber = "73"
|
||||
landmarkName = "setUp()"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
@@ -57,8 +57,8 @@
|
||||
filePath = "Soyuz/Views/SoyuzMenuBarExtraView.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "14"
|
||||
endingLineNumber = "14"
|
||||
startingLineNumber = "15"
|
||||
endingLineNumber = "15"
|
||||
landmarkName = "SoyuzMenuBarExtraView"
|
||||
landmarkType = "14">
|
||||
</BreakpointContent>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// Persistence.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// KlipperMonApp.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@@ -15,6 +16,11 @@ struct SoyuzApp: App {
|
||||
@ObservedObject static var printerManager = MoonrakerSocketManager()
|
||||
|
||||
var body: some Scene {
|
||||
// WindowGroup(id: "floating-stats") {
|
||||
// KlipperMonMenuBarExtraView(currentMenuBarIcon: $currentIcon)
|
||||
// .environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
// }
|
||||
|
||||
WindowGroup("Configuration", id: "soyuz_cfg", content: {
|
||||
PrinterConfigView(printerManager: SoyuzApp.printerManager)
|
||||
//.frame(minWidth: 300, maxWidth: 600, minHeight: 60, maxHeight: 100)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// BonjourBrowser.swift
|
||||
// Soyuz
|
||||
//
|
||||
// Created by maddiefuzz on 3/20/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
@@ -88,3 +89,50 @@ class BonjourBrowser: ObservableObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//class BonjourBrowser: ObservableObject {
|
||||
// @Published var NDEngineResults: [NWBrowser.Result] = []
|
||||
//
|
||||
// private let nwBrowser: NetworkDiscoveryEngine
|
||||
// var connection: NWConnection!
|
||||
//
|
||||
// // TEMPORARY
|
||||
//// var bonjourListener: NWListener?
|
||||
//
|
||||
// init(browser: NetworkDiscoveryEngine = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)) {
|
||||
// nwBrowser = browser
|
||||
// // Bonjour browser results changed handler
|
||||
// nwBrowser.setBrowseResultsChangedHandler({ (newResults, changes) in
|
||||
// print("[update] Results changed.")
|
||||
// self.NDEngineResults.removeAll()
|
||||
// newResults.forEach { result in
|
||||
// print(result)
|
||||
// self.NDEngineResults.append(result)
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// // Bonjour browser state update handler
|
||||
// nwBrowser.setStateUpdateHandler({ newState in
|
||||
// switch newState {
|
||||
// case .failed(let error):
|
||||
// print("[error] nwbrowser: \(error)")
|
||||
// case .ready:
|
||||
// print("[ready] nwbrowser")
|
||||
// case .setup:
|
||||
// print("[setup] nwbrowser")
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// nwBrowser.startScan(queue: DispatchQueue.main)
|
||||
// }
|
||||
//
|
||||
// func startScanning(queue: DispatchQueue.main) {
|
||||
// if(self.nwBrowser.state == NWBrowser.State.cancelled) {
|
||||
//
|
||||
// }
|
||||
// self.nwBrowser.startScan(queue)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// MoonrakerSocketManager.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
@@ -68,6 +69,11 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate {
|
||||
print("\(key): \(value)")
|
||||
})
|
||||
|
||||
// if isConnected == true {
|
||||
// connection?.cancel()
|
||||
// socket?.disconnect()
|
||||
// }
|
||||
//
|
||||
if connection == nil || connection?.state == .cancelled {
|
||||
connection = NWConnection(to: endpoint, using: .tcp)
|
||||
}
|
||||
|
||||
207
Soyuz/ViewModels/MoonrakerSocketManagerNative.swift
Normal file
207
Soyuz/ViewModels/MoonrakerSocketManagerNative.swift
Normal file
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// MoonrakerSocketManagerNative.swift
|
||||
// Soyuz
|
||||
//
|
||||
// Created by Madeline Pace on 6/27/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import AppKit
|
||||
//import Starscream
|
||||
|
||||
|
||||
class MoonrakerSocketManagerNative: ObservableObject {
|
||||
let WEBSOCKET_TIMEOUT_INTERVAL: TimeInterval = 60.0
|
||||
|
||||
// Websocket JSON-RPC published data
|
||||
@Published var state: String
|
||||
@Published var progress: Double
|
||||
@Published var extruderTemperature: Double
|
||||
@Published var bedTemperature: Double
|
||||
|
||||
// Active connection published data
|
||||
@Published var isConnected = false
|
||||
@Published var socketHost: String
|
||||
@Published var socketPort: String
|
||||
|
||||
// Published NWConnection for listing connection information
|
||||
@Published var connection: NWConnection?
|
||||
@Published var friendlyHostname: String = ""
|
||||
|
||||
var notification = UserNotificationHandler.shared
|
||||
|
||||
private var socket: NWConnection?
|
||||
private var lastPingDate = Date()
|
||||
|
||||
// MARK: PRM init()
|
||||
init() {
|
||||
state = ""
|
||||
progress = 0.0
|
||||
extruderTemperature = 0.0
|
||||
bedTemperature = 0.0
|
||||
socketHost = ""
|
||||
socketPort = ""
|
||||
|
||||
// Set up sleep/wake notification observers
|
||||
let center = NSWorkspace.shared.notificationCenter;
|
||||
let mainQueue = OperationQueue.main
|
||||
|
||||
center.addObserver(forName: NSWorkspace.screensDidWakeNotification, object: nil, queue: mainQueue) { notification in
|
||||
self.screenChangedSleepState(notification)
|
||||
}
|
||||
|
||||
center.addObserver(forName: NSWorkspace.screensDidSleepNotification, object: nil, queue: mainQueue) { notification in
|
||||
self.screenChangedSleepState(notification)
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the UI with an endpoint.
|
||||
// Momentarily connect/disconnects from the endpoint to retrieve the host/port
|
||||
// calls private function openWebsocket to process the host/port
|
||||
func connectToBonjourEndpoint(_ endpoint: NWEndpoint) {
|
||||
// Debug stuff
|
||||
endpoint.txtRecord?.forEach({ (key: String, value: NWTXTRecord.Entry) in
|
||||
print("\(key): \(value)")
|
||||
})
|
||||
|
||||
print("About to connect to endpoint \(endpoint.debugDescription)")
|
||||
|
||||
if connection == nil || connection?.state == .cancelled {
|
||||
// //let parameters = NWParameters.tcp
|
||||
// //let options = NWProtocolWebSocket.Options()
|
||||
// //parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
|
||||
// let paramet
|
||||
connection = NWConnection(to: endpoint, using: .tcp)
|
||||
}
|
||||
//
|
||||
// connection!.stateUpdateHandler = { [self] state in
|
||||
// switch state {
|
||||
// case .setup:
|
||||
// break
|
||||
// case .ready:
|
||||
// self.isConnected = true
|
||||
// case .failed(let error):
|
||||
// self.isConnected = false
|
||||
// print("[NWConnection websocket] connection failed: \(error)")
|
||||
// case .cancelled:
|
||||
// self.isConnected = false
|
||||
// print("Connection cancelled.")
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//
|
||||
connection?.stateUpdateHandler = { [self] state in
|
||||
switch state {
|
||||
case .ready:
|
||||
if let innerEndpoint = connection?.currentPath?.remoteEndpoint, case .hostPort(let host, let port) = innerEndpoint {
|
||||
let hostPortDebugOutput = "Connected to \(host):\(port)"
|
||||
|
||||
print(hostPortDebugOutput)
|
||||
|
||||
let hostString = "\(host)"
|
||||
let regex = try! Regex("%(.+)")
|
||||
let match = hostString.firstMatch(of: regex)
|
||||
|
||||
let sanitizedHost = hostString.replacingOccurrences(of: match?.0 ?? "", with: "")
|
||||
|
||||
print("[sanitized] Resolved \(sanitizedHost):\(port)")
|
||||
|
||||
connection?.cancel()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.friendlyHostname = endpoint.toFriendlyString()
|
||||
self.socketHost = sanitizedHost
|
||||
self.socketPort = "\(port)"
|
||||
self.openWebsocket()
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
connection?.start(queue: .global())
|
||||
}
|
||||
|
||||
func disconnect() {
|
||||
print("disconnect() called")
|
||||
self.isConnected = false
|
||||
//socket?.disconnect()
|
||||
//socket = nil
|
||||
}
|
||||
|
||||
func openWebsocket() {
|
||||
let parameters = NWParameters.tcp
|
||||
let socketUrl = URL(string: "ws://\(socketHost):\(socketPort)/websocket")
|
||||
let options = NWProtocolWebSocket.Options()
|
||||
parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
|
||||
socket = NWConnection(to: .url(socketUrl!), using: parameters)
|
||||
socket?.stateUpdateHandler = { state in
|
||||
switch state {
|
||||
case .setup:
|
||||
print("[websocket] Connection setup.")
|
||||
case .ready:
|
||||
print("[websocket] Connection ready.")
|
||||
case .failed(let error):
|
||||
print("[websocket] Connection failed: \(error)")
|
||||
case .cancelled:
|
||||
print("[websocket] Connection cancelled.")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
socket?.start(queue: .global())
|
||||
}
|
||||
|
||||
func socketConnectionChangedState() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TODO: This may not work properly when already connected to the socket
|
||||
private func reconnectWebsocket() {
|
||||
if socket == nil {
|
||||
print("Socket doesn't exist. Fail-safe triggered.")
|
||||
return
|
||||
}
|
||||
|
||||
//socket!.disconnect()
|
||||
//self.openWebsocket()
|
||||
}
|
||||
|
||||
// MARK: Callbacks
|
||||
func screenChangedSleepState(_ notification: Notification) {
|
||||
switch(notification.name) {
|
||||
case NSWorkspace.screensDidSleepNotification:
|
||||
print("Screen slept. Disconnecting..")
|
||||
self.disconnect()
|
||||
//socket?.disconnect()
|
||||
case NSWorkspace.screensDidWakeNotification:
|
||||
print("Screen awoke. Opening websocket..")
|
||||
//self.openWebsocket()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Properly formatted JSON-RPC Request for use with Starscream
|
||||
// MARK: JSON-RPC Request Codable
|
||||
//struct JsonRpcRequest: Codable {
|
||||
// var jsonrpc = "2.0"
|
||||
// let method: String
|
||||
// let params: [String: [String: String?]]
|
||||
// var id = 1
|
||||
//
|
||||
// func encode(to encoder: Encoder) throws {
|
||||
// var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
// try container.encode(jsonrpc, forKey: .jsonrpc)
|
||||
// try container.encode(method, forKey: .method)
|
||||
// try container.encode(params, forKey: .params)
|
||||
// try container.encode(id, forKey: .id)
|
||||
// }
|
||||
//}
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// PrinterObjectsQuery.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// UserNotificationProtocol.swift
|
||||
// Soyuz
|
||||
//
|
||||
// Created by Madeline Pace on 5/28/23.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// PrinterConfigView.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/8/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Network
|
||||
@@ -12,8 +13,11 @@ struct PrinterConfigView: View {
|
||||
@ObservedObject var printerManager: MoonrakerSocketManager
|
||||
@ObservedObject var bonjourBrowser = BonjourBrowser()
|
||||
|
||||
//@State var bonjourBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)
|
||||
|
||||
@Environment(\.openURL) private var openURL
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if(printerManager.isConnected) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// KlipperMonMenuBarExtraView.swift
|
||||
// KlipperMon
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UserNotifications
|
||||
@@ -76,6 +77,7 @@ struct SoyuzMenuBarExtraView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
//.frame(minWidth: 220, minHeight: 100)
|
||||
// Footer information
|
||||
HStack {
|
||||
Button {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// BonjourBrowserTests.swift
|
||||
// SoyuzTests
|
||||
//
|
||||
// Created by maddiefuzz on 3/24/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Network
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// MoonrakerSocketManagerTests.swift
|
||||
// SoyuzTests
|
||||
//
|
||||
// Created by maddiefuzz on 2/21/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import Starscream
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// KlipperMonTests.swift
|
||||
// KlipperMonTests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import Soyuz
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// KlipperMonUITests.swift
|
||||
// KlipperMonUITests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
//
|
||||
// KlipperMonUITestsLaunchTests.swift
|
||||
// KlipperMonUITests
|
||||
//
|
||||
// Created by maddiefuzz on 2/7/23.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
|
||||
Reference in New Issue
Block a user