mirror of
https://github.com/yJason/ClashX-Dashboard.git
synced 2026-03-01 00:35:19 +08:00
Merge branch 'spm' into dev
This commit is contained in:
commit
e5860ead2c
64 files changed
+1555
-707
No files matched your search
@@ -0,0 +1,9 @@
|
||||
.DS_Store
|
||||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/config/registries.json
|
||||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||
.netrc
|
||||
@@ -0,0 +1,47 @@
|
||||
// swift-tools-version: 5.7
|
||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "ClashX Dashboard Kit",
|
||||
platforms: [
|
||||
.macOS(.v12),
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
name: "ClashX Dashboard Kit",
|
||||
targets: ["ClashX Dashboard Kit"]),
|
||||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0"),
|
||||
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "5.0.0"),
|
||||
.package(url: "https://github.com/daltoniam/Starscream.git", exact: "3.1.1"),
|
||||
.package(url: "https://github.com/CocoaLumberjack/CocoaLumberjack.git", from: "3.0.0"),
|
||||
.package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.0.0"),
|
||||
.package(url: "https://github.com/dagronf/DSFSparkline.git", from: "4.0.0"),
|
||||
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.2.3"),
|
||||
],
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "ClashX Dashboard Kit",
|
||||
dependencies: [
|
||||
"Alamofire",
|
||||
"CocoaLumberjack",
|
||||
.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"),
|
||||
"DifferenceKit",
|
||||
"DSFSparkline",
|
||||
.product(name: "Introspect", package: "SwiftUI-Introspect"),
|
||||
"Starscream",
|
||||
"SwiftyJSON",
|
||||
|
||||
]),
|
||||
.testTarget(
|
||||
name: "ClashX Dashboard KitTests",
|
||||
dependencies: ["ClashX Dashboard Kit"]),
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,3 @@
|
||||
# ClashX Dashboard Kit
|
||||
|
||||
A description of this package.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,13 @@
|
||||
import SwiftUI
|
||||
|
||||
@available(macOS 12.0, *)
|
||||
public struct DashboardView: View {
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// DashboardViewContoller.swift
|
||||
// ClashX
|
||||
//
|
||||
// Created by yicheng on 2018/8/28.
|
||||
// Copyright © 2018年 west2online. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
|
||||
public class DashboardWindowController: NSWindowController {
|
||||
public var onWindowClose: (() -> Void)?
|
||||
|
||||
public static func create() -> DashboardWindowController {
|
||||
let win = NSWindow()
|
||||
win.center()
|
||||
let wc = DashboardWindowController(window: win)
|
||||
wc.contentViewController = DashboardViewContoller()
|
||||
return wc
|
||||
}
|
||||
|
||||
public override func showWindow(_ sender: Any?) {
|
||||
super.showWindow(sender)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
window?.makeKeyAndOrderFront(self)
|
||||
window?.delegate = self
|
||||
}
|
||||
|
||||
public func set(_ apiURL: String, secret: String? = nil) {
|
||||
ConfigManager.shared.isRunning = true
|
||||
ConfigManager.shared.overrideApiURL = .init(string: apiURL)
|
||||
ConfigManager.shared.overrideSecret = secret
|
||||
}
|
||||
|
||||
public func reload() {
|
||||
NotificationCenter.default.post(name: .reloadDashboard, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension DashboardWindowController: NSWindowDelegate {
|
||||
public func windowWillClose(_ notification: Notification) {
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
onWindowClose?()
|
||||
if let contentVC = contentViewController as? DashboardViewContoller, let win = window {
|
||||
if !win.styleMask.contains(.fullScreen) {
|
||||
contentVC.lastSize = win.frame.size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardViewContoller: NSViewController {
|
||||
let contentView = NSHostingView(rootView: DashboardView())
|
||||
let minSize = NSSize(width: 920, height: 580)
|
||||
var lastSize: CGSize? {
|
||||
set {
|
||||
if let size = newValue {
|
||||
UserDefaults.standard.set(NSStringFromSize(size), forKey: "ClashWebViewContoller.lastSize")
|
||||
}
|
||||
}
|
||||
get {
|
||||
if let str = UserDefaults.standard.value(forKey: "ClashWebViewContoller.lastSize") as? String {
|
||||
return NSSizeFromString(str) as CGSize
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
let effectView = NSVisualEffectView()
|
||||
|
||||
private let levels = [
|
||||
ClashLogLevel.silent,
|
||||
.error,
|
||||
.warning,
|
||||
.info,
|
||||
.debug
|
||||
]
|
||||
|
||||
private var sidebarItemObserver: NSObjectProtocol?
|
||||
|
||||
func createWindowController() -> NSWindowController {
|
||||
let sb = NSStoryboard(name: "Main", bundle: Bundle.main)
|
||||
let vc = sb.instantiateController(withIdentifier: "DashboardViewContoller") as! DashboardViewContoller
|
||||
let wc = NSWindowController(window: NSWindow())
|
||||
wc.contentViewController = vc
|
||||
return wc
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
view = contentView
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
sidebarItemObserver = NotificationCenter.default.addObserver(forName: .sidebarItemChanged, object: nil, queue: .main) {
|
||||
guard let item = $0.userInfo?["item"] as? SidebarItem else { return }
|
||||
|
||||
var items = [NSToolbarItem.Identifier]()
|
||||
items.append(.toggleSidebar)
|
||||
|
||||
switch item {
|
||||
case .overview, .config:
|
||||
break
|
||||
case .proxies, .providers:
|
||||
items.append(.hideNamesItem)
|
||||
items.append(.searchItem)
|
||||
case .rules:
|
||||
items.append(.searchItem)
|
||||
case .conns:
|
||||
items.append(.stopConnsItem)
|
||||
items.append(.searchItem)
|
||||
case .logs:
|
||||
items.append(.logLevelItem)
|
||||
items.append(.searchItem)
|
||||
}
|
||||
self.reinitToolbar(items)
|
||||
}
|
||||
}
|
||||
|
||||
public override func viewWillAppear() {
|
||||
super.viewWillAppear()
|
||||
view.window?.styleMask.insert(.fullSizeContentView)
|
||||
|
||||
view.window?.isOpaque = false
|
||||
view.window?.styleMask.insert(.closable)
|
||||
view.window?.styleMask.insert(.resizable)
|
||||
view.window?.styleMask.insert(.miniaturizable)
|
||||
|
||||
let toolbar = NSToolbar(identifier: .init("DashboardToolbar"))
|
||||
toolbar.displayMode = .iconOnly
|
||||
toolbar.delegate = self
|
||||
|
||||
view.window?.toolbar = toolbar
|
||||
view.window?.title = "Dashboard"
|
||||
reinitToolbar([])
|
||||
|
||||
view.window?.minSize = minSize
|
||||
if let lastSize = lastSize, lastSize != .zero {
|
||||
view.window?.setContentSize(lastSize)
|
||||
}
|
||||
view.window?.center()
|
||||
if NSApp.activationPolicy() == .accessory {
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
}
|
||||
}
|
||||
|
||||
func reinitToolbar(_ items: [NSToolbarItem.Identifier]) {
|
||||
guard let toolbar = view.window?.toolbar else { return }
|
||||
|
||||
toolbar.items.enumerated().reversed().forEach {
|
||||
toolbar.removeItem(at: $0.offset)
|
||||
}
|
||||
|
||||
items.reversed().forEach {
|
||||
toolbar.insertItem(withItemIdentifier: $0, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let sidebarItemObserver {
|
||||
NotificationCenter.default.removeObserver(sidebarItemObserver)
|
||||
}
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension NSToolbarItem.Identifier {
|
||||
static let hideNamesItem = NSToolbarItem.Identifier("HideNamesItem")
|
||||
static let stopConnsItem = NSToolbarItem.Identifier("StopConnsItem")
|
||||
static let logLevelItem = NSToolbarItem.Identifier("LogLevelItem")
|
||||
static let searchItem = NSToolbarItem.Identifier("SearchItem")
|
||||
}
|
||||
|
||||
extension DashboardViewContoller: NSSearchFieldDelegate {
|
||||
|
||||
func controlTextDidChange(_ obj: Notification) {
|
||||
guard let obj = obj.object as? NSSearchField else { return }
|
||||
NotificationCenter.default.post(name: .toolbarSearchString, object: nil, userInfo: ["String": obj.stringValue])
|
||||
}
|
||||
|
||||
@IBAction func stopConns(_ sender: NSToolbarItem) {
|
||||
NotificationCenter.default.post(name: .stopConns, object: nil)
|
||||
}
|
||||
|
||||
@IBAction func hideNames(_ sender: NSToolbarItem) {
|
||||
switch sender.tag {
|
||||
case 0:
|
||||
sender.tag = 1
|
||||
sender.image = NSImage(systemSymbolName: "eyeglasses", accessibilityDescription: nil)
|
||||
case 1:
|
||||
sender.tag = 0
|
||||
sender.image = NSImage(systemSymbolName: "wand.and.stars", accessibilityDescription: nil)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .hideNames, object: nil, userInfo: ["hide": sender.tag == 1])
|
||||
}
|
||||
|
||||
@objc func setLogLevel(_ sender: NSToolbarItemGroup) {
|
||||
guard sender.selectedIndex < levels.count, sender.selectedIndex >= 0 else { return }
|
||||
let level = levels[sender.selectedIndex]
|
||||
|
||||
NotificationCenter.default.post(name: .logLevelChanged, object: nil, userInfo: ["level": level])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension DashboardViewContoller: NSToolbarDelegate, NSToolbarItemValidation {
|
||||
|
||||
func validateToolbarItem(_ item: NSToolbarItem) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
|
||||
|
||||
switch itemIdentifier {
|
||||
case .searchItem:
|
||||
let item = NSSearchToolbarItem(itemIdentifier: .searchItem)
|
||||
item.resignsFirstResponderWithCancel = true
|
||||
item.searchField.delegate = self
|
||||
item.toolTip = "Search"
|
||||
return item
|
||||
case .toggleSidebar:
|
||||
return NSTrackingSeparatorToolbarItem(itemIdentifier: .toggleSidebar)
|
||||
case .logLevelItem:
|
||||
|
||||
let titles = levels.map {
|
||||
$0.rawValue.capitalized
|
||||
}
|
||||
|
||||
let group = NSToolbarItemGroup(itemIdentifier: .logLevelItem, titles: titles, selectionMode: .selectOne, labels: titles, target: nil, action: #selector(setLogLevel(_:)))
|
||||
group.selectionMode = .selectOne
|
||||
group.controlRepresentation = .collapsed
|
||||
group.selectedIndex = levels.firstIndex(of: ConfigManager.selectLoggingApiLevel) ?? 0
|
||||
|
||||
return group
|
||||
case .hideNamesItem:
|
||||
let item = NSToolbarItem(itemIdentifier: .hideNamesItem)
|
||||
item.target = self
|
||||
item.action = #selector(hideNames(_:))
|
||||
item.isBordered = true
|
||||
item.tag = 0
|
||||
item.image = NSImage(systemSymbolName: "wand.and.stars", accessibilityDescription: nil)
|
||||
return item
|
||||
case .stopConnsItem:
|
||||
let item = NSToolbarItem(itemIdentifier: .stopConnsItem)
|
||||
item.target = self
|
||||
item.action = #selector(stopConns(_:))
|
||||
item.isBordered = true
|
||||
item.image = NSImage(systemSymbolName: "stop.circle.fill", accessibilityDescription: nil)
|
||||
return item
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
[
|
||||
.toggleSidebar,
|
||||
.stopConnsItem,
|
||||
.hideNamesItem,
|
||||
.logLevelItem,
|
||||
.searchItem
|
||||
]
|
||||
}
|
||||
|
||||
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
|
||||
[
|
||||
.toggleSidebar,
|
||||
.stopConnsItem,
|
||||
.hideNamesItem,
|
||||
.logLevelItem,
|
||||
.searchItem
|
||||
]
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// NotificationNames.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension NSNotification.Name {
|
||||
static let reloadDashboard = NSNotification.Name("ReloadDashboard")
|
||||
static let sidebarItemChanged = NSNotification.Name("SidebarItemChanged")
|
||||
|
||||
static let toolbarSearchString = NSNotification.Name("ToolbarSearchString")
|
||||
static let stopConns = NSNotification.Name("StopConns")
|
||||
static let hideNames = NSNotification.Name("HideNames")
|
||||
static let logLevelChanged = NSNotification.Name("LogLevelChanged")
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+10
-6
@@ -7,6 +7,7 @@
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
import CocoaLumberjackSwift
|
||||
import DifferenceKit
|
||||
|
||||
class ClashApiDatasStorage: NSObject, ObservableObject {
|
||||
|
||||
@@ -125,14 +126,17 @@ class ClashOverviewData: ObservableObject, Identifiable {
|
||||
class ClashLogStorage: ObservableObject {
|
||||
@Published var logs = [ClashLog]()
|
||||
|
||||
class ClashLog: NSObject, ObservableObject, Identifiable {
|
||||
class ClashLog: NSObject, ObservableObject, Identifiable, Differentiable {
|
||||
let id: String
|
||||
var differenceIdentifier: String {
|
||||
return id
|
||||
}
|
||||
|
||||
let date: Date
|
||||
let level: ClashLogLevel
|
||||
@objc let log: String
|
||||
|
||||
let levelColor: Color
|
||||
let levelColor: NSColor
|
||||
@objc let levelString: String
|
||||
|
||||
init(level: String, log: String) {
|
||||
@@ -144,13 +148,13 @@ class ClashLogStorage: ObservableObject {
|
||||
self.levelString = level
|
||||
switch self.level {
|
||||
case .info:
|
||||
levelColor = .blue
|
||||
levelColor = .systemBlue
|
||||
case .warning:
|
||||
levelColor = .yellow
|
||||
levelColor = .systemYellow
|
||||
case .error:
|
||||
levelColor = .red
|
||||
levelColor = .systemRed
|
||||
case .debug:
|
||||
levelColor = .green
|
||||
levelColor = .systemGreen
|
||||
default:
|
||||
levelColor = .white
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
+54
-5
@@ -48,6 +48,7 @@ struct CollectionsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
let scrollView = NonRespondingScrollView()
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = true
|
||||
scrollView.autohidesScrollers = true
|
||||
|
||||
let tableView = NonRespondingTableView()
|
||||
tableView.usesAlternatingRowBackgroundColors = true
|
||||
@@ -55,8 +56,6 @@ struct CollectionsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
tableView.delegate = context.coordinator
|
||||
tableView.dataSource = context.coordinator
|
||||
|
||||
tableView.register(.init(nibNamed: .init("CollectionTableCellView"), bundle: .main), forIdentifier: .init(rawValue: "CollectionTableCellView"))
|
||||
|
||||
TableColumn.allCases.forEach {
|
||||
let tableColumn = NSTableColumn(identifier: .init($0.rawValue))
|
||||
tableColumn.title = $0.rawValue
|
||||
@@ -174,14 +173,15 @@ struct CollectionsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
guard let cell = tableView.makeView(withIdentifier: .init(rawValue: "CollectionTableCellView"), owner: nil) as? NSTableCellView,
|
||||
|
||||
guard let cellView = tableView.createCellView(with: "ConnsTableCellView"),
|
||||
let s = tableColumn?.identifier.rawValue.split(separator: ".").last,
|
||||
let tc = TableColumn(rawValue: String(s))
|
||||
else { return nil }
|
||||
|
||||
let conn = conns[row]
|
||||
|
||||
cell.textField?.objectValue = {
|
||||
cellView.textField?.objectValue = {
|
||||
switch tc {
|
||||
case .host:
|
||||
return conn.host
|
||||
@@ -208,7 +208,7 @@ struct CollectionsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
}
|
||||
}()
|
||||
|
||||
return cell
|
||||
return cellView
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
|
||||
@@ -216,6 +216,7 @@ struct CollectionsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,4 +269,52 @@ extension NSTableView {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func createCellView(with identifier: String) -> NSTableCellView? {
|
||||
// https://stackoverflow.com/a/27624927
|
||||
|
||||
var cellView: NSTableCellView?
|
||||
if let spareView = makeView(withIdentifier: .init(identifier),
|
||||
owner: self) as? NSTableCellView {
|
||||
|
||||
// We can use an old cell - no need to do anything.
|
||||
cellView = spareView
|
||||
|
||||
} else {
|
||||
|
||||
// Create a text field for the cell
|
||||
let textField = NSTextField()
|
||||
textField.backgroundColor = NSColor.clear
|
||||
textField.translatesAutoresizingMaskIntoConstraints = false
|
||||
textField.isBordered = false
|
||||
textField.font = .systemFont(ofSize: 13)
|
||||
textField.lineBreakMode = .byTruncatingTail
|
||||
|
||||
// Create a cell
|
||||
let newCell = NSTableCellView()
|
||||
newCell.identifier = .init(identifier)
|
||||
newCell.addSubview(textField)
|
||||
newCell.textField = textField
|
||||
|
||||
// Constrain the text field within the cell
|
||||
newCell.addConstraints(
|
||||
NSLayoutConstraint.constraints(withVisualFormat: "H:|[textField]|",
|
||||
options: [],
|
||||
metrics: nil,
|
||||
views: ["textField" : textField]))
|
||||
|
||||
newCell.addConstraint(.init(item: textField, attribute: .centerY, relatedBy: .equal, toItem: newCell, attribute: .centerY, multiplier: 1, constant: 0))
|
||||
|
||||
|
||||
textField.bind(NSBindingName.value,
|
||||
to: newCell,
|
||||
withKeyPath: "objectValue",
|
||||
options: nil)
|
||||
|
||||
cellView = newCell
|
||||
}
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
}
|
||||
File renamed without changes.
+12
-1
@@ -18,16 +18,27 @@ struct ConnectionsView: View {
|
||||
filterString: searchString)
|
||||
.background(Color(nsColor: .textBackgroundColor))
|
||||
.searchable(text: $searchString)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {
|
||||
guard let string = $0.userInfo?["String"] as? String else { return }
|
||||
searchString = string
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .stopConns)) { _ in
|
||||
stopConns()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button {
|
||||
ApiRequest.closeAllConnection()
|
||||
stopConns()
|
||||
} label: {
|
||||
Image(systemName: "stop.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stopConns() {
|
||||
ApiRequest.closeAllConnection()
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionsView_Previews: PreviewProvider {
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// LogsTableView.swift
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
import DifferenceKit
|
||||
|
||||
struct LogsTableView<Item: Hashable>: NSViewRepresentable {
|
||||
|
||||
enum TableColumn: String, CaseIterable {
|
||||
case date = "Date"
|
||||
case level = "Level"
|
||||
case log = "Log"
|
||||
}
|
||||
|
||||
var data: [Item]
|
||||
var filterString: String
|
||||
|
||||
class NonRespondingScrollView: NSScrollView {
|
||||
override var acceptsFirstResponder: Bool { false }
|
||||
}
|
||||
|
||||
class NonRespondingTableView: NSTableView {
|
||||
override var acceptsFirstResponder: Bool { false }
|
||||
}
|
||||
|
||||
func makeNSView(context: Context) -> NSScrollView {
|
||||
|
||||
let scrollView = NonRespondingScrollView()
|
||||
scrollView.hasVerticalScroller = true
|
||||
scrollView.hasHorizontalScroller = false
|
||||
scrollView.autohidesScrollers = true
|
||||
|
||||
let tableView = NonRespondingTableView()
|
||||
tableView.usesAlternatingRowBackgroundColors = true
|
||||
|
||||
tableView.delegate = context.coordinator
|
||||
tableView.dataSource = context.coordinator
|
||||
|
||||
TableColumn.allCases.forEach {
|
||||
let tableColumn = NSTableColumn(identifier: .init($0.rawValue))
|
||||
tableColumn.title = $0.rawValue
|
||||
tableColumn.isEditable = false
|
||||
|
||||
switch $0 {
|
||||
case .date:
|
||||
tableColumn.minWidth = 60
|
||||
tableColumn.maxWidth = 140
|
||||
tableColumn.width = 135
|
||||
case .level:
|
||||
tableColumn.minWidth = 40
|
||||
tableColumn.maxWidth = 65
|
||||
default:
|
||||
tableColumn.minWidth = 120
|
||||
tableColumn.maxWidth = .infinity
|
||||
}
|
||||
|
||||
tableView.addTableColumn(tableColumn)
|
||||
}
|
||||
|
||||
scrollView.documentView = tableView
|
||||
|
||||
return scrollView
|
||||
}
|
||||
|
||||
func updateNSView(_ nsView: NSScrollView, context: Context) {
|
||||
context.coordinator.parent = self
|
||||
guard let tableView = nsView.documentView as? NSTableView,
|
||||
let data = data as? [ClashLogStorage.ClashLog] else {
|
||||
return
|
||||
}
|
||||
|
||||
let target = updateSorts(data, tableView: tableView)
|
||||
|
||||
let source = context.coordinator.logs
|
||||
let changeset = StagedChangeset(source: source, target: target)
|
||||
|
||||
|
||||
tableView.reload(using: changeset) { data in
|
||||
context.coordinator.logs = data
|
||||
}
|
||||
}
|
||||
|
||||
func updateSorts(_ objects: [ClashLogStorage.ClashLog],
|
||||
tableView: NSTableView) -> [ClashLogStorage.ClashLog] {
|
||||
var re = objects
|
||||
|
||||
let filterKeys = [
|
||||
"levelString",
|
||||
"log",
|
||||
]
|
||||
|
||||
re = re.filtered(filterString, for: filterKeys)
|
||||
|
||||
return re
|
||||
}
|
||||
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(parent: self)
|
||||
}
|
||||
|
||||
|
||||
class Coordinator: NSObject, NSTableViewDelegate, NSTableViewDataSource {
|
||||
|
||||
var parent: LogsTableView
|
||||
var logs = [ClashLogStorage.ClashLog]()
|
||||
|
||||
let dateFormatter = {
|
||||
let df = DateFormatter()
|
||||
df.dateFormat = "MM/dd HH:mm:ss.SSS"
|
||||
return df
|
||||
}()
|
||||
|
||||
|
||||
init(parent: LogsTableView) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
logs.count
|
||||
}
|
||||
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
|
||||
guard let cellView = tableView.createCellView(with: "LogsTableCellView"),
|
||||
let s = tableColumn?.identifier.rawValue.split(separator: ".").last,
|
||||
let tc = TableColumn(rawValue: String(s))
|
||||
else { return nil }
|
||||
|
||||
let log = logs[row]
|
||||
let tf = cellView.textField
|
||||
|
||||
switch tc {
|
||||
case .date:
|
||||
tf?.lineBreakMode = .byTruncatingHead
|
||||
tf?.textColor = .orange
|
||||
tf?.stringValue = dateFormatter.string(from: log.date)
|
||||
case .level:
|
||||
tf?.lineBreakMode = .byTruncatingTail
|
||||
tf?.textColor = log.levelColor
|
||||
tf?.stringValue = log.levelString
|
||||
case .log:
|
||||
tf?.lineBreakMode = .byTruncatingTail
|
||||
tf?.textColor = .labelColor
|
||||
tf?.stringValue = log.log
|
||||
}
|
||||
|
||||
return cellView
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// LogsView.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LogsView: View {
|
||||
|
||||
@EnvironmentObject var logStorage: ClashLogStorage
|
||||
|
||||
@State var searchString: String = ""
|
||||
@State var logLevel = ConfigManager.selectLoggingApiLevel
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
LogsTableView(data: logStorage.logs.reversed(), filterString: searchString)
|
||||
}
|
||||
.searchable(text: $searchString)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {
|
||||
guard let string = $0.userInfo?["String"] as? String else { return }
|
||||
searchString = string
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .logLevelChanged)) {
|
||||
guard let level = $0.userInfo?["level"] as? ClashLogLevel else { return }
|
||||
logLevelChanged(level)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Picker("", selection: $logLevel) {
|
||||
ForEach([
|
||||
ClashLogLevel.silent,
|
||||
.error,
|
||||
.warning,
|
||||
.info,
|
||||
.debug
|
||||
], id: \.self) {
|
||||
Text($0.rawValue.capitalized).tag($0)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
.onChange(of: logLevel) { newValue in
|
||||
guard newValue != ConfigManager.selectLoggingApiLevel else { return }
|
||||
logLevelChanged(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logLevelChanged(_ level: ClashLogLevel) {
|
||||
logStorage.logs.removeAll()
|
||||
ConfigManager.selectLoggingApiLevel = level
|
||||
ApiRequest.shared.resetLogStreamApi()
|
||||
}
|
||||
}
|
||||
|
||||
struct LogsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LogsView()
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
@@ -20,6 +20,14 @@ struct ProvidersView: View {
|
||||
EmptyView()
|
||||
}
|
||||
.searchable(text: $searchString.string)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {
|
||||
guard let string = $0.userInfo?["String"] as? String else { return }
|
||||
searchString.string = string
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .hideNames)) {
|
||||
guard let hide = $0.userInfo?["hide"] as? Bool else { return }
|
||||
hideProxyNames.hide = hide
|
||||
}
|
||||
.environmentObject(searchString)
|
||||
.onAppear {
|
||||
loadProviders()
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
@@ -34,6 +34,14 @@ struct ProxiesView: View {
|
||||
EmptyView()
|
||||
}
|
||||
.searchable(text: $searchString.string)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {
|
||||
guard let string = $0.userInfo?["String"] as? String else { return }
|
||||
searchString.string = string
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .hideNames)) {
|
||||
guard let hide = $0.userInfo?["hide"] as? Bool else { return }
|
||||
hideProxyNames.hide = hide
|
||||
}
|
||||
.environmentObject(searchString)
|
||||
.onAppear {
|
||||
loadProxies()
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+4
@@ -29,6 +29,10 @@ struct RulesView: View {
|
||||
}
|
||||
}
|
||||
.searchable(text: $searchString)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .toolbarSearchString)) {
|
||||
guard let string = $0.userInfo?["String"] as? String else { return }
|
||||
searchString = string
|
||||
}
|
||||
.onAppear {
|
||||
ruleItems.removeAll()
|
||||
ApiRequest.getRules {
|
||||
File renamed without changes.
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// SidebarItem.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
|
||||
enum SidebarItem: String {
|
||||
case overview = "Overview"
|
||||
case proxies = "Proxies"
|
||||
case providers = "Providers"
|
||||
case rules = "Rules"
|
||||
case conns = "Conns"
|
||||
case config = "Config"
|
||||
case logs = "Logs"
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// SidebarListView.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Introspect
|
||||
|
||||
struct SidebarListView: View {
|
||||
|
||||
@Binding var selectionName: SidebarItem?
|
||||
|
||||
@State private var reloadID = UUID().uuidString
|
||||
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
NavigationLink(destination: OverviewView(),
|
||||
tag: SidebarItem.overview,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.overview.rawValue, systemImage: "chart.bar.xaxis")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ProxiesView(),
|
||||
tag: SidebarItem.proxies,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.proxies.rawValue, systemImage: "globe.asia.australia")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ProvidersView(),
|
||||
tag: SidebarItem.providers,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.providers.rawValue, systemImage: "link.icloud")
|
||||
}
|
||||
|
||||
NavigationLink(destination: RulesView(),
|
||||
tag: SidebarItem.rules,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.rules.rawValue, systemImage: "waveform.and.magnifyingglass")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ConnectionsView(),
|
||||
tag: SidebarItem.conns,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.conns.rawValue, systemImage: "app.connected.to.app.below.fill")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ConfigView(),
|
||||
tag: SidebarItem.config,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.config.rawValue, systemImage: "slider.horizontal.3")
|
||||
}
|
||||
|
||||
NavigationLink(destination: LogsView(),
|
||||
tag: SidebarItem.logs,
|
||||
selection: $selectionName) {
|
||||
Label(SidebarItem.logs.rawValue, systemImage: "wand.and.stars.inverse")
|
||||
}
|
||||
|
||||
}
|
||||
.introspectTableView {
|
||||
if selectionName == nil {
|
||||
selectionName = SidebarItem.overview
|
||||
$0.allowsEmptySelection = false
|
||||
if $0.selectedRow == -1 {
|
||||
$0.selectRowIndexes(.init(integer: 0), byExtendingSelection: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
.id(reloadID)
|
||||
.onReceive(NotificationCenter.default.publisher(for: .reloadDashboard)) { _ in
|
||||
reloadID = UUID().uuidString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//struct SidebarListView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// SidebarListView()
|
||||
// }
|
||||
//}
|
||||
+12
-2
@@ -13,10 +13,10 @@ struct SidebarView: View {
|
||||
private let connsQueue = DispatchQueue(label: "thread-safe-connsQueue", attributes: .concurrent)
|
||||
private let timer = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
|
||||
|
||||
@State private var sidebarSelectionName: String? = "Overview"
|
||||
@State private var sidebarSelectionName: SidebarItem?
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { scrollViewProxy in
|
||||
Group {
|
||||
SidebarListView(selectionName: $sidebarSelectionName)
|
||||
}
|
||||
.environmentObject(clashApiDatasStorage.overviewData)
|
||||
@@ -35,9 +35,13 @@ struct SidebarView: View {
|
||||
|
||||
updateConnections()
|
||||
}
|
||||
.onChange(of: sidebarSelectionName) { newValue in
|
||||
sidebarItemChanged(newValue)
|
||||
}
|
||||
.onReceive(timer, perform: { _ in
|
||||
updateConnections()
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func updateConnections() {
|
||||
@@ -50,6 +54,12 @@ struct SidebarView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sidebarItemChanged(_ item: SidebarItem?) {
|
||||
guard let item else { return }
|
||||
|
||||
NotificationCenter.default.post(name: .sidebarItemChanged, object: nil, userInfo: ["item": item])
|
||||
}
|
||||
}
|
||||
|
||||
//struct SidebarView_Previews: PreviewProvider {
|
||||
File renamed without changes.
@@ -0,0 +1,11 @@
|
||||
import XCTest
|
||||
@testable import ClashX_Dashboard_Kit
|
||||
|
||||
final class ClashX_Dashboard_KitTests: XCTestCase {
|
||||
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.
|
||||
XCTAssertEqual(ClashX_Dashboard_Kit().text, "Hello, World!")
|
||||
}
|
||||
}
|
||||
@@ -7,122 +7,23 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
010F693B29ED639A00BAAFB5 /* ClashServerAppStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010F693A29ED639A00BAAFB5 /* ClashServerAppStorage.swift */; };
|
||||
01359A392A21D88B00A2B3FB /* SidebarListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01359A382A21D88B00A2B3FB /* SidebarListView.swift */; };
|
||||
01505C4A2A147B84001ACC4F /* DBProviderStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01505C492A147B84001ACC4F /* DBProviderStorage.swift */; };
|
||||
01505C4C2A14A1A3001ACC4F /* ProviderRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01505C4B2A14A1A3001ACC4F /* ProviderRowView.swift */; };
|
||||
01505C4E2A14AAEB001ACC4F /* ProviderProxiesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01505C4D2A14AAEB001ACC4F /* ProviderProxiesView.swift */; };
|
||||
015278082A15F9FD00516236 /* ProxyProviderInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015278072A15F9FD00516236 /* ProxyProviderInfoView.swift */; };
|
||||
0155D39629F2342F00869830 /* TrafficGraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0155D39529F2342F00869830 /* TrafficGraphView.swift */; };
|
||||
0155D39829F23BDE00869830 /* OverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0155D39729F23BDE00869830 /* OverviewView.swift */; };
|
||||
0172CB5129E5AE670072DDEF /* SwiftUIViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172CB5029E5AE670072DDEF /* SwiftUIViewExtensions.swift */; };
|
||||
0172F12F2A1FB06100EE2B6D /* DateFormatter+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F12C2A1FB06100EE2B6D /* DateFormatter+.swift */; };
|
||||
0172F1312A1FB06100EE2B6D /* String+Encode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F12E2A1FB06100EE2B6D /* String+Encode.swift */; };
|
||||
0172F1352A1FB0B900EE2B6D /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F1342A1FB0B900EE2B6D /* ConfigManager.swift */; };
|
||||
0172F1372A1FB0CD00EE2B6D /* ApiRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F1362A1FB0CD00EE2B6D /* ApiRequest.swift */; };
|
||||
0172F1392A1FB0E900EE2B6D /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F1382A1FB0E900EE2B6D /* Logger.swift */; };
|
||||
0172F1412A1FB10D00EE2B6D /* DBConnectionSnapShot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F13B2A1FB10C00EE2B6D /* DBConnectionSnapShot.swift */; };
|
||||
0172F1422A1FB10D00EE2B6D /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F13C2A1FB10D00EE2B6D /* ClashRuleProvider.swift */; };
|
||||
0172F1432A1FB10D00EE2B6D /* ClashRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F13D2A1FB10D00EE2B6D /* ClashRule.swift */; };
|
||||
0172F1442A1FB10D00EE2B6D /* ClashConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F13E2A1FB10D00EE2B6D /* ClashConfig.swift */; };
|
||||
0172F1452A1FB10D00EE2B6D /* ClashProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F13F2A1FB10D00EE2B6D /* ClashProvider.swift */; };
|
||||
0172F1462A1FB10D00EE2B6D /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F1402A1FB10D00EE2B6D /* ClashProxy.swift */; };
|
||||
0172F1482A1FB90200EE2B6D /* ClashConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0172F1472A1FB90200EE2B6D /* ClashConnection.swift */; };
|
||||
017753C029EF7FB2006999DB /* APIServerItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017753BF29EF7FB1006999DB /* APIServerItem.swift */; };
|
||||
017DCADD29E83BFD00B9622A /* RuleProviderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017DCADC29E83BFD00B9622A /* RuleProviderView.swift */; };
|
||||
017F9AAA2A0DFEBD00B81497 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 017F9AA92A0DFEBD00B81497 /* Introspect */; };
|
||||
017F9AAC2A0E0B2300B81497 /* ProxyGroupRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 017F9AAB2A0E0B2300B81497 /* ProxyGroupRowView.swift */; };
|
||||
018003B12A136DDB0070226E /* ProvidersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018003B02A136DDB0070226E /* ProvidersView.swift */; };
|
||||
018A61BD29E9A2ED008608C0 /* APISettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018A61BC29E9A2ED008608C0 /* APISettingView.swift */; };
|
||||
018AFEA52A1B5F7A0076E66B /* ProgressButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018AFEA42A1B5F7A0076E66B /* ProgressButton.swift */; };
|
||||
018C836C29E17505006366D3 /* ClashApiDatasStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018C836B29E17505006366D3 /* ClashApiDatasStorage.swift */; };
|
||||
011BFE462A279D1C0027AD15 /* ClashX Dashboard Kit in Frameworks */ = {isa = PBXBuildFile; productRef = 011BFE452A279D1C0027AD15 /* ClashX Dashboard Kit */; };
|
||||
0192315F29DD4DCF00539EDD /* ClashX_DashboardApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192315E29DD4DCF00539EDD /* ClashX_DashboardApp.swift */; };
|
||||
0192316129DD4DCF00539EDD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192316029DD4DCF00539EDD /* ContentView.swift */; };
|
||||
0192316329DD4DD100539EDD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0192316229DD4DD100539EDD /* Assets.xcassets */; };
|
||||
0192316629DD4DD100539EDD /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0192316529DD4DD100539EDD /* Preview Assets.xcassets */; };
|
||||
0192317129DD566000539EDD /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317029DD566000539EDD /* SidebarView.swift */; };
|
||||
0192317829DD5DA500539EDD /* ProxiesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317729DD5DA500539EDD /* ProxiesView.swift */; };
|
||||
0192317A29DD5DB000539EDD /* RulesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317929DD5DB000539EDD /* RulesView.swift */; };
|
||||
0192317C29DD5DF200539EDD /* ConnectionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317B29DD5DF200539EDD /* ConnectionsView.swift */; };
|
||||
0192317E29DD5E0100539EDD /* ConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317D29DD5E0100539EDD /* ConfigView.swift */; };
|
||||
0192318029DD5E0B00539EDD /* LogsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192317F29DD5E0B00539EDD /* LogsView.swift */; };
|
||||
0192318329DD70B400539EDD /* SidebarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192318229DD70B400539EDD /* SidebarItem.swift */; };
|
||||
0192318529DD7DCD00539EDD /* SidebarItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192318429DD7DCD00539EDD /* SidebarItemView.swift */; };
|
||||
0192318729DD83FF00539EDD /* OverviewTopItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0192318629DD83FF00539EDD /* OverviewTopItemView.swift */; };
|
||||
0192B5BA29DE50F8002CDBF3 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 0192B5B929DE50F8002CDBF3 /* Alamofire */; };
|
||||
0192B5BD29DE5113002CDBF3 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 0192B5BC29DE5113002CDBF3 /* SwiftyJSON */; };
|
||||
0192B5C029DE5134002CDBF3 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 0192B5BF29DE5134002CDBF3 /* Starscream */; };
|
||||
0192B5D429DE5190002CDBF3 /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0192B5D329DE5190002CDBF3 /* CocoaLumberjackSwift */; };
|
||||
019D6A8729F015DF00A6AC02 /* ArrayExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019D6A8629F015DF00A6AC02 /* ArrayExtensions.swift */; };
|
||||
019D6A9629F194C600A6AC02 /* DSFSparkline in Frameworks */ = {isa = PBXBuildFile; productRef = 019D6A9529F194C600A6AC02 /* DSFSparkline */; };
|
||||
01A351A229DD8F440054894E /* RuleItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A351A129DD8F440054894E /* RuleItemView.swift */; };
|
||||
01A351A929DD9CB00054894E /* Connections.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A351A829DD9CB00054894E /* Connections.swift */; };
|
||||
01A3EF042A120103003038B5 /* DBProxyStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01A3EF032A120103003038B5 /* DBProxyStorage.swift */; };
|
||||
01CD0A9229E93ABB00F4C17E /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 01CD0A9129E93ABB00F4C17E /* DifferenceKit */; };
|
||||
01DCEFB12A150E8B00DBBDB3 /* RuleProvidersRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DCEFB02A150E8B00DBBDB3 /* RuleProvidersRowView.swift */; };
|
||||
01DCEFB32A150FB300DBBDB3 /* ProxyProvidersRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DCEFB22A150FB300DBBDB3 /* ProxyProvidersRowView.swift */; };
|
||||
01F5E3F42A1E53F4008F3DEB /* ConfigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */; };
|
||||
01F885CF29DFD8DF008241EB /* CollectionsTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F885CE29DFD8DF008241EB /* CollectionsTableView.swift */; };
|
||||
01F885D129E03F20008241EB /* CollectionTableCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01F885D029E03F20008241EB /* CollectionTableCellView.xib */; };
|
||||
01F885D329E04E21008241EB /* ProxyGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F885D229E04E21008241EB /* ProxyGroupView.swift */; };
|
||||
01F885D529E053DE008241EB /* ProxyItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F885D429E053DE008241EB /* ProxyItemView.swift */; };
|
||||
01A78B7F2A2B188F0058D1CA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01A78B7E2A2B188E0058D1CA /* Main.storyboard */; };
|
||||
01FE884C2A29BB590084902C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FE884B2A29BB590084902C /* AppDelegate.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
010F693A29ED639A00BAAFB5 /* ClashServerAppStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashServerAppStorage.swift; sourceTree = "<group>"; };
|
||||
01359A382A21D88B00A2B3FB /* SidebarListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarListView.swift; sourceTree = "<group>"; };
|
||||
01505C492A147B84001ACC4F /* DBProviderStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBProviderStorage.swift; sourceTree = "<group>"; };
|
||||
01505C4B2A14A1A3001ACC4F /* ProviderRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderRowView.swift; sourceTree = "<group>"; };
|
||||
01505C4D2A14AAEB001ACC4F /* ProviderProxiesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProxiesView.swift; sourceTree = "<group>"; };
|
||||
015278072A15F9FD00516236 /* ProxyProviderInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyProviderInfoView.swift; sourceTree = "<group>"; };
|
||||
0155D39529F2342F00869830 /* TrafficGraphView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrafficGraphView.swift; sourceTree = "<group>"; };
|
||||
0155D39729F23BDE00869830 /* OverviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OverviewView.swift; sourceTree = "<group>"; };
|
||||
0172CB5029E5AE670072DDEF /* SwiftUIViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIViewExtensions.swift; sourceTree = "<group>"; };
|
||||
0172F12C2A1FB06100EE2B6D /* DateFormatter+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormatter+.swift"; sourceTree = "<group>"; };
|
||||
0172F12E2A1FB06100EE2B6D /* String+Encode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Encode.swift"; sourceTree = "<group>"; };
|
||||
0172F1342A1FB0B900EE2B6D /* ConfigManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = "<group>"; };
|
||||
0172F1362A1FB0CD00EE2B6D /* ApiRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiRequest.swift; sourceTree = "<group>"; };
|
||||
0172F1382A1FB0E900EE2B6D /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||
0172F13B2A1FB10C00EE2B6D /* DBConnectionSnapShot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DBConnectionSnapShot.swift; sourceTree = "<group>"; };
|
||||
0172F13C2A1FB10D00EE2B6D /* ClashRuleProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashRuleProvider.swift; sourceTree = "<group>"; };
|
||||
0172F13D2A1FB10D00EE2B6D /* ClashRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashRule.swift; sourceTree = "<group>"; };
|
||||
0172F13E2A1FB10D00EE2B6D /* ClashConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashConfig.swift; sourceTree = "<group>"; };
|
||||
0172F13F2A1FB10D00EE2B6D /* ClashProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashProvider.swift; sourceTree = "<group>"; };
|
||||
0172F1402A1FB10D00EE2B6D /* ClashProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = "<group>"; };
|
||||
0172F1472A1FB90200EE2B6D /* ClashConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClashConnection.swift; sourceTree = "<group>"; };
|
||||
017753BF29EF7FB1006999DB /* APIServerItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIServerItem.swift; sourceTree = "<group>"; };
|
||||
017DCADC29E83BFD00B9622A /* RuleProviderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleProviderView.swift; sourceTree = "<group>"; };
|
||||
017F9AAB2A0E0B2300B81497 /* ProxyGroupRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupRowView.swift; sourceTree = "<group>"; };
|
||||
018003B02A136DDB0070226E /* ProvidersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProvidersView.swift; sourceTree = "<group>"; };
|
||||
018A61BC29E9A2ED008608C0 /* APISettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APISettingView.swift; sourceTree = "<group>"; };
|
||||
018AFEA42A1B5F7A0076E66B /* ProgressButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressButton.swift; sourceTree = "<group>"; };
|
||||
018C836B29E17505006366D3 /* ClashApiDatasStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashApiDatasStorage.swift; sourceTree = "<group>"; };
|
||||
011BFE402A2799550027AD15 /* ClashX Dashboard Kit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = "ClashX Dashboard Kit"; sourceTree = "<group>"; };
|
||||
0192315B29DD4DCF00539EDD /* ClashX Dashboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ClashX Dashboard.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0192315E29DD4DCF00539EDD /* ClashX_DashboardApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashX_DashboardApp.swift; sourceTree = "<group>"; };
|
||||
0192316029DD4DCF00539EDD /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
0192316229DD4DD100539EDD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0192316529DD4DD100539EDD /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
0192316729DD4DD100539EDD /* ClashX_Dashboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClashX_Dashboard.entitlements; sourceTree = "<group>"; };
|
||||
0192317029DD566000539EDD /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
|
||||
0192317729DD5DA500539EDD /* ProxiesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxiesView.swift; sourceTree = "<group>"; };
|
||||
0192317929DD5DB000539EDD /* RulesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RulesView.swift; sourceTree = "<group>"; };
|
||||
0192317B29DD5DF200539EDD /* ConnectionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionsView.swift; sourceTree = "<group>"; };
|
||||
0192317D29DD5E0100539EDD /* ConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigView.swift; sourceTree = "<group>"; };
|
||||
0192317F29DD5E0B00539EDD /* LogsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsView.swift; sourceTree = "<group>"; };
|
||||
0192318229DD70B400539EDD /* SidebarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarItem.swift; sourceTree = "<group>"; };
|
||||
0192318429DD7DCD00539EDD /* SidebarItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarItemView.swift; sourceTree = "<group>"; };
|
||||
0192318629DD83FF00539EDD /* OverviewTopItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewTopItemView.swift; sourceTree = "<group>"; };
|
||||
019D6A8629F015DF00A6AC02 /* ArrayExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtensions.swift; sourceTree = "<group>"; };
|
||||
01A351A129DD8F440054894E /* RuleItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleItemView.swift; sourceTree = "<group>"; };
|
||||
01A351A829DD9CB00054894E /* Connections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connections.swift; sourceTree = "<group>"; };
|
||||
01A3EF032A120103003038B5 /* DBProxyStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBProxyStorage.swift; sourceTree = "<group>"; };
|
||||
01DCEFB02A150E8B00DBBDB3 /* RuleProvidersRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleProvidersRowView.swift; sourceTree = "<group>"; };
|
||||
01DCEFB22A150FB300DBBDB3 /* ProxyProvidersRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyProvidersRowView.swift; sourceTree = "<group>"; };
|
||||
01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigItemView.swift; sourceTree = "<group>"; };
|
||||
01F885CE29DFD8DF008241EB /* CollectionsTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionsTableView.swift; sourceTree = "<group>"; };
|
||||
01F885D029E03F20008241EB /* CollectionTableCellView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CollectionTableCellView.xib; sourceTree = "<group>"; };
|
||||
01F885D229E04E21008241EB /* ProxyGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupView.swift; sourceTree = "<group>"; };
|
||||
01F885D429E053DE008241EB /* ProxyItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyItemView.swift; sourceTree = "<group>"; };
|
||||
01A78B7E2A2B188E0058D1CA /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||
01FE884B2A29BB590084902C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -130,109 +31,27 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0192B5BD29DE5113002CDBF3 /* SwiftyJSON in Frameworks */,
|
||||
0192B5BA29DE50F8002CDBF3 /* Alamofire in Frameworks */,
|
||||
019D6A9629F194C600A6AC02 /* DSFSparkline in Frameworks */,
|
||||
0192B5D429DE5190002CDBF3 /* CocoaLumberjackSwift in Frameworks */,
|
||||
017F9AAA2A0DFEBD00B81497 /* Introspect in Frameworks */,
|
||||
01CD0A9229E93ABB00F4C17E /* DifferenceKit in Frameworks */,
|
||||
0192B5C029DE5134002CDBF3 /* Starscream in Frameworks */,
|
||||
011BFE462A279D1C0027AD15 /* ClashX Dashboard Kit in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
010F693929ED638B00BAAFB5 /* APISetting */ = {
|
||||
011BFE442A279D1C0027AD15 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
018A61BC29E9A2ED008608C0 /* APISettingView.swift */,
|
||||
018AFEA42A1B5F7A0076E66B /* ProgressButton.swift */,
|
||||
017753BF29EF7FB1006999DB /* APIServerItem.swift */,
|
||||
010F693A29ED639A00BAAFB5 /* ClashServerAppStorage.swift */,
|
||||
);
|
||||
path = APISetting;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0172F12B2A1FB05900EE2B6D /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0172F12C2A1FB06100EE2B6D /* DateFormatter+.swift */,
|
||||
0172F12E2A1FB06100EE2B6D /* String+Encode.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0172F1322A1FB0AA00EE2B6D /* General */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0172F1332A1FB0B000EE2B6D /* Managers */,
|
||||
);
|
||||
path = General;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0172F1332A1FB0B000EE2B6D /* Managers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0172F1342A1FB0B900EE2B6D /* ConfigManager.swift */,
|
||||
);
|
||||
path = Managers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0172F13A2A1FB10300EE2B6D /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0172F13E2A1FB10D00EE2B6D /* ClashConfig.swift */,
|
||||
0172F1472A1FB90200EE2B6D /* ClashConnection.swift */,
|
||||
0172F13F2A1FB10D00EE2B6D /* ClashProvider.swift */,
|
||||
0172F1402A1FB10D00EE2B6D /* ClashProxy.swift */,
|
||||
0172F13D2A1FB10D00EE2B6D /* ClashRule.swift */,
|
||||
0172F13C2A1FB10D00EE2B6D /* ClashRuleProvider.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
018003AF2A136D3E0070226E /* Providers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
018003B02A136DDB0070226E /* ProvidersView.swift */,
|
||||
01DCEFB22A150FB300DBBDB3 /* ProxyProvidersRowView.swift */,
|
||||
01DCEFB02A150E8B00DBBDB3 /* RuleProvidersRowView.swift */,
|
||||
017DCADC29E83BFD00B9622A /* RuleProviderView.swift */,
|
||||
01505C4B2A14A1A3001ACC4F /* ProviderRowView.swift */,
|
||||
01505C4D2A14AAEB001ACC4F /* ProviderProxiesView.swift */,
|
||||
015278072A15F9FD00516236 /* ProxyProviderInfoView.swift */,
|
||||
);
|
||||
path = Providers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
018C836929E1703D006366D3 /* Logs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317F29DD5E0B00539EDD /* LogsView.swift */,
|
||||
);
|
||||
path = Logs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
018C836A29E174CB006366D3 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192316029DD4DCF00539EDD /* ContentView.swift */,
|
||||
018C836B29E17505006366D3 /* ClashApiDatasStorage.swift */,
|
||||
010F693929ED638B00BAAFB5 /* APISetting */,
|
||||
0192318129DD709500539EDD /* SidebarView */,
|
||||
0192317429DD5D7400539EDD /* ContentTabs */,
|
||||
019D6A8629F015DF00A6AC02 /* ArrayExtensions.swift */,
|
||||
0172CB5029E5AE670072DDEF /* SwiftUIViewExtensions.swift */,
|
||||
);
|
||||
path = Views;
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0192315229DD4DCF00539EDD = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
011BFE402A2799550027AD15 /* ClashX Dashboard Kit */,
|
||||
0192315D29DD4DCF00539EDD /* ClashX Dashboard */,
|
||||
0192315C29DD4DCF00539EDD /* Products */,
|
||||
011BFE442A279D1C0027AD15 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@@ -248,9 +67,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192315E29DD4DCF00539EDD /* ClashX_DashboardApp.swift */,
|
||||
01A3EF022A120032003038B5 /* Models */,
|
||||
018C836A29E174CB006366D3 /* Views */,
|
||||
0192B5B529DE506D002CDBF3 /* ClashX Links */,
|
||||
01FE884B2A29BB590084902C /* AppDelegate.swift */,
|
||||
01A78B7E2A2B188E0058D1CA /* Main.storyboard */,
|
||||
0192316229DD4DD100539EDD /* Assets.xcassets */,
|
||||
0192316729DD4DD100539EDD /* ClashX_Dashboard.entitlements */,
|
||||
0192316429DD4DD100539EDD /* Preview Content */,
|
||||
@@ -266,103 +84,6 @@
|
||||
path = "Preview Content";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0192317429DD5D7400539EDD /* ContentTabs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
01A351A529DD9B2D0054894E /* Overview */,
|
||||
01A351A629DD9BB50054894E /* Proxies */,
|
||||
018003AF2A136D3E0070226E /* Providers */,
|
||||
01A351A029DD8F210054894E /* Rules */,
|
||||
01A351A729DD9C7F0054894E /* Connections */,
|
||||
01A7335F29E2CBD600205699 /* Config */,
|
||||
018C836929E1703D006366D3 /* Logs */,
|
||||
);
|
||||
path = ContentTabs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0192318129DD709500539EDD /* SidebarView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317029DD566000539EDD /* SidebarView.swift */,
|
||||
01359A382A21D88B00A2B3FB /* SidebarListView.swift */,
|
||||
0192318429DD7DCD00539EDD /* SidebarItemView.swift */,
|
||||
0192318229DD70B400539EDD /* SidebarItem.swift */,
|
||||
);
|
||||
path = SidebarView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0192B5B529DE506D002CDBF3 /* ClashX Links */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0172F1362A1FB0CD00EE2B6D /* ApiRequest.swift */,
|
||||
0172F1382A1FB0E900EE2B6D /* Logger.swift */,
|
||||
0172F1322A1FB0AA00EE2B6D /* General */,
|
||||
0172F12B2A1FB05900EE2B6D /* Extensions */,
|
||||
0172F13A2A1FB10300EE2B6D /* Models */,
|
||||
);
|
||||
path = "ClashX Links";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A351A029DD8F210054894E /* Rules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317929DD5DB000539EDD /* RulesView.swift */,
|
||||
01A351A129DD8F440054894E /* RuleItemView.swift */,
|
||||
);
|
||||
path = Rules;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A351A529DD9B2D0054894E /* Overview */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0155D39729F23BDE00869830 /* OverviewView.swift */,
|
||||
0192318629DD83FF00539EDD /* OverviewTopItemView.swift */,
|
||||
0155D39529F2342F00869830 /* TrafficGraphView.swift */,
|
||||
);
|
||||
path = Overview;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A351A629DD9BB50054894E /* Proxies */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317729DD5DA500539EDD /* ProxiesView.swift */,
|
||||
017F9AAB2A0E0B2300B81497 /* ProxyGroupRowView.swift */,
|
||||
01F885D229E04E21008241EB /* ProxyGroupView.swift */,
|
||||
01F885D429E053DE008241EB /* ProxyItemView.swift */,
|
||||
);
|
||||
path = Proxies;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A351A729DD9C7F0054894E /* Connections */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317B29DD5DF200539EDD /* ConnectionsView.swift */,
|
||||
01A351A829DD9CB00054894E /* Connections.swift */,
|
||||
01F885CE29DFD8DF008241EB /* CollectionsTableView.swift */,
|
||||
01F885D029E03F20008241EB /* CollectionTableCellView.xib */,
|
||||
);
|
||||
path = Connections;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A3EF022A120032003038B5 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
01A3EF032A120103003038B5 /* DBProxyStorage.swift */,
|
||||
01505C492A147B84001ACC4F /* DBProviderStorage.swift */,
|
||||
0172F13B2A1FB10C00EE2B6D /* DBConnectionSnapShot.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
01A7335F29E2CBD600205699 /* Config */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0192317D29DD5E0100539EDD /* ConfigView.swift */,
|
||||
01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */,
|
||||
);
|
||||
path = Config;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -380,13 +101,7 @@
|
||||
);
|
||||
name = "ClashX Dashboard";
|
||||
packageProductDependencies = (
|
||||
0192B5B929DE50F8002CDBF3 /* Alamofire */,
|
||||
0192B5BC29DE5113002CDBF3 /* SwiftyJSON */,
|
||||
0192B5BF29DE5134002CDBF3 /* Starscream */,
|
||||
0192B5D329DE5190002CDBF3 /* CocoaLumberjackSwift */,
|
||||
01CD0A9129E93ABB00F4C17E /* DifferenceKit */,
|
||||
019D6A9529F194C600A6AC02 /* DSFSparkline */,
|
||||
017F9AA92A0DFEBD00B81497 /* Introspect */,
|
||||
011BFE452A279D1C0027AD15 /* ClashX Dashboard Kit */,
|
||||
);
|
||||
productName = "ClashX Dashboard";
|
||||
productReference = 0192315B29DD4DCF00539EDD /* ClashX Dashboard.app */;
|
||||
@@ -417,13 +132,6 @@
|
||||
);
|
||||
mainGroup = 0192315229DD4DCF00539EDD;
|
||||
packageReferences = (
|
||||
0192B5B829DE50F8002CDBF3 /* XCRemoteSwiftPackageReference "Alamofire" */,
|
||||
0192B5BB29DE5113002CDBF3 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||
0192B5BE29DE5134002CDBF3 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||
0192B5D229DE5190002CDBF3 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */,
|
||||
01CD0A9029E93ABB00F4C17E /* XCRemoteSwiftPackageReference "DifferenceKit" */,
|
||||
019D6A9429F194C600A6AC02 /* XCRemoteSwiftPackageReference "DSFSparkline" */,
|
||||
017F9AA82A0DFEBD00B81497 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */,
|
||||
);
|
||||
productRefGroup = 0192315C29DD4DCF00539EDD /* Products */;
|
||||
projectDirPath = "";
|
||||
@@ -439,8 +147,8 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
01A78B7F2A2B188F0058D1CA /* Main.storyboard in Resources */,
|
||||
0192316629DD4DD100539EDD /* Preview Assets.xcassets in Resources */,
|
||||
01F885D129E03F20008241EB /* CollectionTableCellView.xib in Resources */,
|
||||
0192316329DD4DD100539EDD /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -452,55 +160,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0192317A29DD5DB000539EDD /* RulesView.swift in Sources */,
|
||||
019D6A8729F015DF00A6AC02 /* ArrayExtensions.swift in Sources */,
|
||||
01505C4C2A14A1A3001ACC4F /* ProviderRowView.swift in Sources */,
|
||||
0192316129DD4DCF00539EDD /* ContentView.swift in Sources */,
|
||||
01DCEFB32A150FB300DBBDB3 /* ProxyProvidersRowView.swift in Sources */,
|
||||
0192318729DD83FF00539EDD /* OverviewTopItemView.swift in Sources */,
|
||||
0192318029DD5E0B00539EDD /* LogsView.swift in Sources */,
|
||||
0172F1392A1FB0E900EE2B6D /* Logger.swift in Sources */,
|
||||
01505C4E2A14AAEB001ACC4F /* ProviderProxiesView.swift in Sources */,
|
||||
0172F1412A1FB10D00EE2B6D /* DBConnectionSnapShot.swift in Sources */,
|
||||
0192318529DD7DCD00539EDD /* SidebarItemView.swift in Sources */,
|
||||
0172F1442A1FB10D00EE2B6D /* ClashConfig.swift in Sources */,
|
||||
0172F1452A1FB10D00EE2B6D /* ClashProvider.swift in Sources */,
|
||||
0192317E29DD5E0100539EDD /* ConfigView.swift in Sources */,
|
||||
0155D39629F2342F00869830 /* TrafficGraphView.swift in Sources */,
|
||||
01A3EF042A120103003038B5 /* DBProxyStorage.swift in Sources */,
|
||||
0172F1432A1FB10D00EE2B6D /* ClashRule.swift in Sources */,
|
||||
017F9AAC2A0E0B2300B81497 /* ProxyGroupRowView.swift in Sources */,
|
||||
0172F1372A1FB0CD00EE2B6D /* ApiRequest.swift in Sources */,
|
||||
017DCADD29E83BFD00B9622A /* RuleProviderView.swift in Sources */,
|
||||
0192318329DD70B400539EDD /* SidebarItem.swift in Sources */,
|
||||
0155D39829F23BDE00869830 /* OverviewView.swift in Sources */,
|
||||
018C836C29E17505006366D3 /* ClashApiDatasStorage.swift in Sources */,
|
||||
018A61BD29E9A2ED008608C0 /* APISettingView.swift in Sources */,
|
||||
0172F1462A1FB10D00EE2B6D /* ClashProxy.swift in Sources */,
|
||||
010F693B29ED639A00BAAFB5 /* ClashServerAppStorage.swift in Sources */,
|
||||
01F885D329E04E21008241EB /* ProxyGroupView.swift in Sources */,
|
||||
01FE884C2A29BB590084902C /* AppDelegate.swift in Sources */,
|
||||
0192315F29DD4DCF00539EDD /* ClashX_DashboardApp.swift in Sources */,
|
||||
018003B12A136DDB0070226E /* ProvidersView.swift in Sources */,
|
||||
01A351A229DD8F440054894E /* RuleItemView.swift in Sources */,
|
||||
01F885D529E053DE008241EB /* ProxyItemView.swift in Sources */,
|
||||
0172F1312A1FB06100EE2B6D /* String+Encode.swift in Sources */,
|
||||
01359A392A21D88B00A2B3FB /* SidebarListView.swift in Sources */,
|
||||
01DCEFB12A150E8B00DBBDB3 /* RuleProvidersRowView.swift in Sources */,
|
||||
01F5E3F42A1E53F4008F3DEB /* ConfigItemView.swift in Sources */,
|
||||
0172F1352A1FB0B900EE2B6D /* ConfigManager.swift in Sources */,
|
||||
0192317129DD566000539EDD /* SidebarView.swift in Sources */,
|
||||
0192317C29DD5DF200539EDD /* ConnectionsView.swift in Sources */,
|
||||
0172F12F2A1FB06100EE2B6D /* DateFormatter+.swift in Sources */,
|
||||
017753C029EF7FB2006999DB /* APIServerItem.swift in Sources */,
|
||||
0172F1422A1FB10D00EE2B6D /* ClashRuleProvider.swift in Sources */,
|
||||
015278082A15F9FD00516236 /* ProxyProviderInfoView.swift in Sources */,
|
||||
0192317829DD5DA500539EDD /* ProxiesView.swift in Sources */,
|
||||
01F885CF29DFD8DF008241EB /* CollectionsTableView.swift in Sources */,
|
||||
0172F1482A1FB90200EE2B6D /* ClashConnection.swift in Sources */,
|
||||
018AFEA52A1B5F7A0076E66B /* ProgressButton.swift in Sources */,
|
||||
01505C4A2A147B84001ACC4F /* DBProviderStorage.swift in Sources */,
|
||||
0172CB5129E5AE670072DDEF /* SwiftUIViewExtensions.swift in Sources */,
|
||||
01A351A929DD9CB00054894E /* Connections.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -633,6 +294,8 @@
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
@@ -659,6 +322,8 @@
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_KEY_NSMainStoryboardFile = Main;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
@@ -695,100 +360,10 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
017F9AA82A0DFEBD00B81497 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.2.3;
|
||||
};
|
||||
};
|
||||
0192B5B829DE50F8002CDBF3 /* XCRemoteSwiftPackageReference "Alamofire" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Alamofire/Alamofire";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.0.0;
|
||||
};
|
||||
};
|
||||
0192B5BB29DE5113002CDBF3 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 5.0.0;
|
||||
};
|
||||
};
|
||||
0192B5BE29DE5134002CDBF3 /* XCRemoteSwiftPackageReference "Starscream" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/daltoniam/Starscream";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 3.1.1;
|
||||
};
|
||||
};
|
||||
0192B5D229DE5190002CDBF3 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 3.0.0;
|
||||
};
|
||||
};
|
||||
019D6A9429F194C600A6AC02 /* XCRemoteSwiftPackageReference "DSFSparkline" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/dagronf/DSFSparkline";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 4.0.0;
|
||||
};
|
||||
};
|
||||
01CD0A9029E93ABB00F4C17E /* XCRemoteSwiftPackageReference "DifferenceKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ra1028/DifferenceKit";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
017F9AA92A0DFEBD00B81497 /* Introspect */ = {
|
||||
011BFE452A279D1C0027AD15 /* ClashX Dashboard Kit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 017F9AA82A0DFEBD00B81497 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */;
|
||||
productName = Introspect;
|
||||
};
|
||||
0192B5B929DE50F8002CDBF3 /* Alamofire */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 0192B5B829DE50F8002CDBF3 /* XCRemoteSwiftPackageReference "Alamofire" */;
|
||||
productName = Alamofire;
|
||||
};
|
||||
0192B5BC29DE5113002CDBF3 /* SwiftyJSON */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 0192B5BB29DE5113002CDBF3 /* XCRemoteSwiftPackageReference "SwiftyJSON" */;
|
||||
productName = SwiftyJSON;
|
||||
};
|
||||
0192B5BF29DE5134002CDBF3 /* Starscream */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 0192B5BE29DE5134002CDBF3 /* XCRemoteSwiftPackageReference "Starscream" */;
|
||||
productName = Starscream;
|
||||
};
|
||||
0192B5D329DE5190002CDBF3 /* CocoaLumberjackSwift */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 0192B5D229DE5190002CDBF3 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */;
|
||||
productName = CocoaLumberjackSwift;
|
||||
};
|
||||
019D6A9529F194C600A6AC02 /* DSFSparkline */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 019D6A9429F194C600A6AC02 /* XCRemoteSwiftPackageReference "DSFSparkline" */;
|
||||
productName = DSFSparkline;
|
||||
};
|
||||
01CD0A9129E93ABB00F4C17E /* DifferenceKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 01CD0A9029E93ABB00F4C17E /* XCRemoteSwiftPackageReference "DifferenceKit" */;
|
||||
productName = DifferenceKit;
|
||||
productName = "ClashX Dashboard Kit";
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import ClashX_Dashboard_Kit
|
||||
|
||||
|
||||
@main
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
var dashboardWindowController: DashboardWindowController?
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
|
||||
if dashboardWindowController == nil {
|
||||
dashboardWindowController = DashboardWindowController.create()
|
||||
dashboardWindowController?.onWindowClose = {
|
||||
[weak self] in
|
||||
self?.dashboardWindowController = nil
|
||||
}
|
||||
}
|
||||
|
||||
dashboardWindowController?.set("http://127.0.0.1:9021")
|
||||
dashboardWindowController?.showWindow(nil)
|
||||
}
|
||||
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ClashX_Dashboard_Kit
|
||||
|
||||
@main
|
||||
//@main
|
||||
struct ClashX_DashboardApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
DashboardView()
|
||||
}
|
||||
.commands {
|
||||
SidebarCommands()
|
||||
|
||||
@@ -0,0 +1,684 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
<scene sceneID="JPo-4y-FX3">
|
||||
<objects>
|
||||
<application id="hnw-xV-0zn" sceneMemberID="viewController">
|
||||
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Fire" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Fire" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About Fire" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Services" id="NMo-om-nkz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
|
||||
<menuItem title="Hide Fire" keyEquivalent="h" id="Olw-nP-bQN">
|
||||
<connections>
|
||||
<action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||
<menuItem title="Quit Fire" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="dMs-cI-mzQ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="File" id="bib-Uj-vzu">
|
||||
<items>
|
||||
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
|
||||
<connections>
|
||||
<action selector="newDocument:" target="Ady-hI-5gd" id="4Si-XN-c54"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
|
||||
<connections>
|
||||
<action selector="openDocument:" target="Ady-hI-5gd" id="bVn-NM-KNZ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Open Recent" id="tXI-mr-wws">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
|
||||
<items>
|
||||
<menuItem title="Clear Menu" id="vNY-rz-j42">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="Daa-9d-B3U"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
|
||||
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
|
||||
<connections>
|
||||
<action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
|
||||
<connections>
|
||||
<action selector="saveDocument:" target="Ady-hI-5gd" id="teZ-XB-qJY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
|
||||
<connections>
|
||||
<action selector="saveDocumentAs:" target="Ady-hI-5gd" id="mDf-zr-I0C"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
|
||||
<connections>
|
||||
<action selector="revertDocumentToSaved:" target="Ady-hI-5gd" id="iJ3-Pv-kwq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
|
||||
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="runPageLayout:" target="Ady-hI-5gd" id="Din-rz-gC5"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
|
||||
<connections>
|
||||
<action selector="print:" target="Ady-hI-5gd" id="qaZ-4w-aoO"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="5QF-Oa-p0T">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
|
||||
<connections>
|
||||
<action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||
<connections>
|
||||
<action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
|
||||
<connections>
|
||||
<action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||
<connections>
|
||||
<action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||
<connections>
|
||||
<action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||
<connections>
|
||||
<action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
|
||||
<menuItem title="Find" id="4EN-yA-p0u">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Find" id="1b7-l0-nxx">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="cD7-Qs-BN4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="WD3-Gg-5AJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="NDo-RZ-v9R"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="HOh-sY-3ay"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
|
||||
<connections>
|
||||
<action selector="performFindPanelAction:" target="Ady-hI-5gd" id="U76-nv-p5D"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||
<connections>
|
||||
<action selector="centerSelectionInVisibleArea:" target="Ady-hI-5gd" id="IOG-6D-g5B"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
|
||||
<connections>
|
||||
<action selector="showGuessPanel:" target="Ady-hI-5gd" id="vFj-Ks-hy3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||
<connections>
|
||||
<action selector="checkSpelling:" target="Ady-hI-5gd" id="fz7-VC-reM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
|
||||
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleContinuousSpellChecking:" target="Ady-hI-5gd" id="7w6-Qz-0kB"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleGrammarChecking:" target="Ady-hI-5gd" id="muD-Qn-j4w"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" target="Ady-hI-5gd" id="2lM-Qi-WAP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="9ic-FL-obx">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontSubstitutionsPanel:" target="Ady-hI-5gd" id="oku-mr-iSq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
|
||||
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleSmartInsertDelete:" target="Ady-hI-5gd" id="3IJ-Se-DZD"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" target="Ady-hI-5gd" id="ptq-xd-QOA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDashSubstitution:" target="Ady-hI-5gd" id="oCt-pO-9gS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticLinkDetection:" target="Ady-hI-5gd" id="Gip-E3-Fov"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticDataDetection:" target="Ady-hI-5gd" id="R1I-Nq-Kbl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleAutomaticTextReplacement:" target="Ady-hI-5gd" id="DvP-Fe-Py6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="2oI-Rn-ZJC">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="uppercaseWord:" target="Ady-hI-5gd" id="sPh-Tk-edu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowercaseWord:" target="Ady-hI-5gd" id="iUZ-b5-hil"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="capitalizeWord:" target="Ady-hI-5gd" id="26H-TL-nsh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="xrE-MZ-jX0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Format" id="jxT-CU-nIS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
|
||||
<items>
|
||||
<menuItem title="Font" id="Gi5-1S-RQB">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
|
||||
<items>
|
||||
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
|
||||
<connections>
|
||||
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
|
||||
<connections>
|
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
|
||||
<connections>
|
||||
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
|
||||
<connections>
|
||||
<action selector="underline:" target="Ady-hI-5gd" id="FYS-2b-JAY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
|
||||
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
|
||||
<connections>
|
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
|
||||
<connections>
|
||||
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
|
||||
<menuItem title="Kern" id="jBQ-r6-VK2">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="GUa-eO-cwY">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useStandardKerning:" target="Ady-hI-5gd" id="6dk-9l-Ckg"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="cDB-IK-hbR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="turnOffKerning:" target="Ady-hI-5gd" id="U8a-gz-Maa"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Tighten" id="46P-cB-AYj">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="tightenKerning:" target="Ady-hI-5gd" id="hr7-Nz-8ro"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Loosen" id="ogc-rX-tC1">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="loosenKerning:" target="Ady-hI-5gd" id="8i4-f9-FKE"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Ligatures" id="o6e-r0-MWq">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="agt-UL-0e3">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useStandardLigatures:" target="Ady-hI-5gd" id="7uR-wd-Dx6"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="J7y-lM-qPV">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="turnOffLigatures:" target="Ady-hI-5gd" id="iX2-gA-Ilz"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Use All" id="xQD-1f-W4t">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="useAllLigatures:" target="Ady-hI-5gd" id="KcB-kA-TuK"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Baseline" id="OaQ-X3-Vso">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="3Om-Ey-2VK">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="unscript:" target="Ady-hI-5gd" id="0vZ-95-Ywn"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Superscript" id="Rqc-34-cIF">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="superscript:" target="Ady-hI-5gd" id="3qV-fo-wpU"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Subscript" id="I0S-gh-46l">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="subscript:" target="Ady-hI-5gd" id="Q6W-4W-IGz"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Raise" id="2h7-ER-AoG">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="raiseBaseline:" target="Ady-hI-5gd" id="4sk-31-7Q9"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Lower" id="1tx-W0-xDw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="lowerBaseline:" target="Ady-hI-5gd" id="OF1-bc-KW4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
|
||||
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
|
||||
<connections>
|
||||
<action selector="orderFrontColorPanel:" target="Ady-hI-5gd" id="mSX-Xz-DV3"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
|
||||
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="copyFont:" target="Ady-hI-5gd" id="GJO-xA-L4q"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteFont:" target="Ady-hI-5gd" id="JfD-CL-leO"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Text" id="Fal-I4-PZk">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Text" id="d9c-me-L2H">
|
||||
<items>
|
||||
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
|
||||
<connections>
|
||||
<action selector="alignLeft:" target="Ady-hI-5gd" id="zUv-R1-uAa"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
|
||||
<connections>
|
||||
<action selector="alignCenter:" target="Ady-hI-5gd" id="spX-mk-kcS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Justify" id="J5U-5w-g23">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="alignJustified:" target="Ady-hI-5gd" id="ljL-7U-jND"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
|
||||
<connections>
|
||||
<action selector="alignRight:" target="Ady-hI-5gd" id="r48-bG-YeY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
|
||||
<menuItem title="Writing Direction" id="H1b-Si-o9J">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
|
||||
<items>
|
||||
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="YGs-j5-SAR">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionNatural:" target="Ady-hI-5gd" id="qtV-5e-UBP"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="Lbh-J2-qVU">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="S0X-9S-QSf"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="jFq-tB-4Kx">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeBaseWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="5fk-qB-AqJ"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
|
||||
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="Nop-cj-93Q">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionNatural:" target="Ady-hI-5gd" id="lPI-Se-ZHp"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="BgM-ve-c93">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionLeftToRight:" target="Ady-hI-5gd" id="caW-Bv-w94"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem id="RB4-Sm-HuC">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="makeTextWritingDirectionRightToLeft:" target="Ady-hI-5gd" id="EXD-6r-ZUu"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
|
||||
<menuItem title="Show Ruler" id="vLm-3I-IUL">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="toggleRuler:" target="Ady-hI-5gd" id="FOx-HJ-KwY"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="copyRuler:" target="Ady-hI-5gd" id="71i-fW-3W2"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="pasteRuler:" target="Ady-hI-5gd" id="cSh-wd-qM2"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="H8h-7b-M4v">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="View" id="HyV-fh-RgO">
|
||||
<items>
|
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleToolbarShown:" target="Ady-hI-5gd" id="BXY-wc-z0C"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="runToolbarCustomizationPalette:" target="Ady-hI-5gd" id="pQI-g3-MTW"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="aUF-d1-5bR">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
|
||||
<connections>
|
||||
<action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="wpr-3q-Mcd">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
|
||||
<items>
|
||||
<menuItem title="Fire Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||
<connections>
|
||||
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||
</connections>
|
||||
</application>
|
||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="ClashX_Dashboard" customModuleProvider="target"/>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="75" y="0.0"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<tableCellView identifier="CollectionTableCellView" id="E1E-Yw-tO2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="100" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="EkO-g0-3Rf">
|
||||
<rect key="frame" x="0.0" y="1" width="100" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="mZD-CG-RaE">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="EkO-g0-3Rf" secondAttribute="trailing" constant="2" id="cZb-2s-5cP"/>
|
||||
<constraint firstItem="EkO-g0-3Rf" firstAttribute="centerY" secondItem="E1E-Yw-tO2" secondAttribute="centerY" id="g8v-Gi-bjM"/>
|
||||
<constraint firstItem="EkO-g0-3Rf" firstAttribute="leading" secondItem="E1E-Yw-tO2" secondAttribute="leading" constant="2" id="nDQ-TP-ijK"/>
|
||||
</constraints>
|
||||
<accessibility identifier="CollectionTableCellView"/>
|
||||
<connections>
|
||||
<outlet property="textField" destination="EkO-g0-3Rf" id="Kzu-HK-Onq"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-108" y="-102"/>
|
||||
</tableCellView>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// LogsView.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LogsView: View {
|
||||
|
||||
@EnvironmentObject var logStorage: ClashLogStorage
|
||||
|
||||
@State var searchString: String = ""
|
||||
@State var logLevel = ConfigManager.selectLoggingApiLevel
|
||||
|
||||
var logs: [ClashLogStorage.ClashLog] {
|
||||
let logs: [ClashLogStorage.ClashLog] = logStorage.logs.reversed()
|
||||
if searchString.isEmpty {
|
||||
return logs
|
||||
} else {
|
||||
return logs.filtered(searchString, for: ["log", "levelString"])
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Table(logs) {
|
||||
TableColumn("Date") {
|
||||
Text($0.date.formatted(
|
||||
Date.FormatStyle()
|
||||
.year(.twoDigits)
|
||||
.month(.twoDigits)
|
||||
.day(.twoDigits)
|
||||
.hour(.twoDigits(amPM: .omitted))
|
||||
.minute(.twoDigits)
|
||||
.second(.twoDigits)
|
||||
))
|
||||
.foregroundColor(.orange)
|
||||
.truncationMode(.head)
|
||||
}
|
||||
.width(min: 60, max: 130)
|
||||
TableColumn("Level") {
|
||||
Text("[\($0.level.rawValue)]")
|
||||
.foregroundColor($0.levelColor)
|
||||
}
|
||||
.width(min: 40, max: 65)
|
||||
TableColumn("", value: \.log)
|
||||
}
|
||||
.searchable(text: $searchString)
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Picker("", selection: $logLevel) {
|
||||
ForEach([
|
||||
ClashLogLevel.silent,
|
||||
.error,
|
||||
.warning,
|
||||
.info,
|
||||
.debug
|
||||
], id: \.self) {
|
||||
Text($0.rawValue.capitalized).tag($0)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.menu)
|
||||
.onChange(of: logLevel) { newValue in
|
||||
guard newValue != ConfigManager.selectLoggingApiLevel else { return }
|
||||
logStorage.logs.removeAll()
|
||||
ConfigManager.selectLoggingApiLevel = newValue
|
||||
ApiRequest.shared.resetLogStreamApi()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LogsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
LogsView()
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// SidebarItem.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import SwiftUI
|
||||
|
||||
|
||||
class SidebarItems: ObservableObject, Identifiable {
|
||||
let id = UUID()
|
||||
@Published var items: [SidebarItem]
|
||||
@Published var selectedIndex = 0
|
||||
|
||||
init(_ items: [SidebarItem]) {
|
||||
self.items = items
|
||||
}
|
||||
}
|
||||
|
||||
class SidebarItem: ObservableObject {
|
||||
let id = UUID()
|
||||
let name: String
|
||||
let icon: String
|
||||
let view: AnyView
|
||||
|
||||
|
||||
init(name: String, icon: String, view: AnyView) {
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
self.view = view
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// SidebarItemView.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SidebarItemView: View {
|
||||
|
||||
@State var item: SidebarItem
|
||||
|
||||
@Binding var selectionName: String?
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(destination: item.view, tag: item.name, selection: $selectionName) {
|
||||
Label(item.name, systemImage: item.icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//struct SidebarItemView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// SidebarItemView(item: .init(name: "Overview",
|
||||
// icon: "chart.bar.xaxis",
|
||||
// view: AnyView(OverviewView())))
|
||||
// }
|
||||
//}
|
||||
@@ -1,68 +0,0 @@
|
||||
//
|
||||
// SidebarListView.swift
|
||||
// ClashX Dashboard
|
||||
//
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SidebarListView: View {
|
||||
|
||||
@Binding var selectionName: String?
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
|
||||
NavigationLink(destination: OverviewView(),
|
||||
tag: "Overview",
|
||||
selection: $selectionName) {
|
||||
Label("Overview", systemImage: "chart.bar.xaxis")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ProxiesView(),
|
||||
tag: "Proxies",
|
||||
selection: $selectionName) {
|
||||
Label("Proxies", systemImage: "globe.asia.australia")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ProvidersView(),
|
||||
tag: "Providers",
|
||||
selection: $selectionName) {
|
||||
Label("Providers", systemImage: "link.icloud")
|
||||
}
|
||||
|
||||
NavigationLink(destination: RulesView(),
|
||||
tag: "Rules",
|
||||
selection: $selectionName) {
|
||||
Label("Rules", systemImage: "waveform.and.magnifyingglass")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ConnectionsView(),
|
||||
tag: "Conns",
|
||||
selection: $selectionName) {
|
||||
Label("Conns", systemImage: "app.connected.to.app.below.fill")
|
||||
}
|
||||
|
||||
NavigationLink(destination: ConfigView(),
|
||||
tag: "Config",
|
||||
selection: $selectionName) {
|
||||
Label("Config", systemImage: "slider.horizontal.3")
|
||||
}
|
||||
|
||||
NavigationLink(destination: LogsView(),
|
||||
tag: "Logs",
|
||||
selection: $selectionName) {
|
||||
Label("Logs", systemImage: "wand.and.stars.inverse")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
}
|
||||
|
||||
//struct SidebarListView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// SidebarListView()
|
||||
// }
|
||||
//}
|
||||
Reference in new issue
Block a user