Compare commits
2 commits
c4c342dc8b
...
453dd2d116
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453dd2d116 | ||
|
|
a757ac7be9 |
35 changed files with 1461 additions and 324 deletions
|
|
@ -46,7 +46,6 @@ opt_in_rules:
|
||||||
- multiline_parameters
|
- multiline_parameters
|
||||||
- multiline_parameters_brackets
|
- multiline_parameters_brackets
|
||||||
- no_extension_access_modifier
|
- no_extension_access_modifier
|
||||||
- no_grouping_extension
|
|
||||||
- nslocalizedstring_key
|
- nslocalizedstring_key
|
||||||
- nslocalizedstring_require_bundle
|
- nslocalizedstring_require_bundle
|
||||||
- number_separator
|
- number_separator
|
||||||
|
|
@ -76,9 +75,7 @@ opt_in_rules:
|
||||||
- sorted_first_last
|
- sorted_first_last
|
||||||
- sorted_imports
|
- sorted_imports
|
||||||
- static_operator
|
- static_operator
|
||||||
- strict_fileprivate
|
|
||||||
- strong_iboutlet
|
- strong_iboutlet
|
||||||
- switch_case_on_newline
|
|
||||||
- test_case_accessibility
|
- test_case_accessibility
|
||||||
- toggle_bool
|
- toggle_bool
|
||||||
- trailing_closure
|
- trailing_closure
|
||||||
|
|
@ -97,3 +94,5 @@ disabled_rules:
|
||||||
- force_try
|
- force_try
|
||||||
- nesting
|
- nesting
|
||||||
- todo
|
- todo
|
||||||
|
- trailing_comma
|
||||||
|
- switch_case_on_newline
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,12 @@ INFOPLIST_KEY_UIStatusBarStyle[sdk=iphone*] = UIStatusBarStyleDefault
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone[sdk=iphone*] = UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight
|
||||||
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
|
TARGETED_DEVICE_FAMILY[sdk=iphone*] = 1,2
|
||||||
|
EXCLUDED_SOURCE_FILE_NAMES = MainMenu.xib
|
||||||
|
|
||||||
|
EXCLUDED_SOURCE_FILE_NAMES[sdk=macosx*] =
|
||||||
|
INFOPLIST_KEY_LSUIElement[sdk=macosx*] = YES
|
||||||
|
INFOPLIST_KEY_NSMainNibFile[sdk=macosx*] = MainMenu
|
||||||
|
INFOPLIST_KEY_NSPrincipalClass[sdk=macosx*] = NSApplication
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities
|
INFOPLIST_KEY_LSApplicationCategoryType[sdk=macosx*] = public.app-category.utilities
|
||||||
|
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements
|
CODE_SIGN_ENTITLEMENTS = App/App-iOS.entitlements
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
private let quitItem: NSMenuItem = {
|
private let quitItem: NSMenuItem = {
|
||||||
let quitItem = NSMenuItem(
|
let quitItem = NSMenuItem(
|
||||||
|
|
@ -16,7 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private let toggleItem: NSMenuItem = {
|
private let toggleItem: NSMenuItem = {
|
||||||
let toggleView = NSHostingView(rootView: MenuItemToggleView(tunnel: BurrowApp.tunnel))
|
let toggleView = NSHostingView(rootView: MenuItemToggleView())
|
||||||
toggleView.frame.size = CGSize(width: 300, height: 32)
|
toggleView.frame.size = CGSize(width: 300, height: 32)
|
||||||
toggleView.autoresizingMask = [.width]
|
toggleView.autoresizingMask = [.width]
|
||||||
|
|
||||||
|
|
|
||||||
20
Apple/App/Assets.xcassets/HackClub.colorset/Contents.json
Normal file
20
Apple/App/Assets.xcassets/HackClub.colorset/Contents.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x50",
|
||||||
|
"green" : "0x37",
|
||||||
|
"red" : "0xEC"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Apple/App/Assets.xcassets/HackClub.imageset/Contents.json
vendored
Normal file
12
Apple/App/Assets.xcassets/HackClub.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "flag-standalone-wtransparent.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf
vendored
Normal file
BIN
Apple/App/Assets.xcassets/HackClub.imageset/flag-standalone-wtransparent.pdf
vendored
Normal file
Binary file not shown.
20
Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json
Normal file
20
Apple/App/Assets.xcassets/WireGuard.colorset/Contents.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0x1A",
|
||||||
|
"green" : "0x17",
|
||||||
|
"red" : "0x88"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json
vendored
Normal file
15
Apple/App/Assets.xcassets/WireGuard.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "WireGuard.svg",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg
vendored
Normal file
6
Apple/App/Assets.xcassets/WireGuard.imageset/WireGuard.svg
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg viewBox="0 0 46 79" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g stroke-width=".265" transform="matrix(1, 0, 0, 1, 144.316635, -78.301682)">
|
||||||
|
<path d="M -131.805 103.359 C -123.863 98.5 -113.717 101.469 -109.915 108.776 C -109.195 110.161 -109.103 112.293 -109.559 113.746 C -111.135 118.761 -114.855 121.574 -119.961 122.769 C -118.455 121.48 -117.257 120.019 -116.876 117.999 C -116.474 116.064 -116.911 114.05 -118.078 112.455 C -119.937 109.901 -123.263 108.888 -126.23 109.971 C -129.373 111.164 -131.095 114.033 -130.785 117.56 C -130.497 120.836 -128.011 122.959 -123.36 123.765 C -124.056 124.133 -124.591 124.404 -125.115 124.696 C -127.245 125.863 -129.099 127.475 -130.55 129.423 C -131.022 130.06 -131.347 130.112 -132.066 129.672 C -141.415 123.955 -142.016 109.605 -131.806 103.359 L -131.805 103.359 Z M -138.803 138.688 C -140.305 139.07 -141.761 139.634 -143.296 140.138 C -142.545 135.071 -136.612 130.404 -131.594 130.936 C -133.048 132.939 -133.896 135.317 -134.039 137.787 C -135.707 138.094 -137.278 138.301 -138.803 138.688 Z M -106.844 89.217 C -105.36 89.271 -103.873 89.248 -102.388 89.284 C -102.017 89.308 -101.649 89.359 -101.285 89.437 C -101.617 89.947 -101.992 90.428 -102.406 90.875 C -102.937 91.37 -103.537 91.853 -104.302 91.101 C -104.486 90.92 -104.921 90.962 -105.241 90.958 C -106.718 90.938 -108.197 90.891 -109.672 90.947 C -110.951 90.988 -112.227 91.118 -113.488 91.336 C -113.725 91.379 -114.078 92.165 -113.969 92.455 C -113.713 93.139 -113.339 93.893 -112.785 94.331 C -110.737 95.947 -108.559 97.399 -106.501 99.004 C -104.502 100.564 -102.641 102.274 -101.507 104.628 C -100.03 107.694 -99.987 110.91 -100.624 114.139 C -101.688 119.531 -104.416 123.998 -108.835 127.242 C -110.615 128.55 -112.819 129.292 -114.858 130.231 C -116.652 131.057 -118.498 131.769 -120.295 132.586 C -123.536 134.06 -125.357 137.577 -124.822 141.235 C -124.33 144.591 -121.386 147.392 -118.013 147.97 C -113.967 148.664 -109.792 146.034 -108.802 141.922 C -107.689 137.297 -110.202 133.168 -114.905 131.917 C -115.112 131.862 -115.32 131.81 -115.752 131.698 C -114.494 131.136 -113.407 130.735 -112.404 130.183 C -110.654 129.22 -108.936 128.201 -107.249 127.124 C -106.754 126.807 -106.486 126.807 -106.063 127.173 C -102.828 129.969 -100.899 133.448 -100.358 137.713 C -99.462 144.773 -102.804 151.259 -109.108 154.584 C -118.86 159.727 -130.794 153.873 -132.948 143.061 C -134.794 133.799 -128.257 125.399 -120.391 123.777 C -117.008 123.079 -113.914 121.671 -111.509 119.065 C -109.957 117.384 -109.205 115.942 -108.948 115.291 C -108.471 114.071 -108.227 112.772 -108.228 111.462 C -108.28 110.329 -108.546 109.216 -109.013 108.182 C -109.834 106.31 -112.98 103.332 -113.759 102.704 L -121.168 96.904 C -121.429 96.689 -121.723 96.705 -122.36 96.748 C -123.117 96.799 -125.053 96.906 -125.888 96.688 C -125.212 96.176 -123.371 95.432 -122.58 94.834 C -124.98 93.212 -127.721 93.798 -130.237 93.313 C -129.655 92.23 -126.776 90.564 -125.139 90.379 C -125.236 89.464 -125.385 88.556 -125.585 87.659 C -125.685 87.291 -126.096 86.934 -126.455 86.723 C -127.324 86.214 -128.246 85.793 -129.246 85.286 C -128.35 84.707 -127.313 84.386 -126.247 84.358 C -125.238 84.32 -124.228 84.418 -123.245 84.651 C -121.461 85.058 -120.037 84.792 -118.618 83.58 C -119.735 83.13 -120.852 82.719 -121.935 82.233 C -123.003 81.746 -124.043 81.202 -125.052 80.604 C -122.241 80.994 -119.523 82.048 -116.649 81.663 C -116.625 81.532 -116.6 81.402 -116.576 81.271 C -118.724 80.771 -120.873 80.271 -123.251 79.717 C -119.272 79.353 -115.567 79.293 -112.059 81.002 C -111.072 81.482 -110.039 81.88 -109.093 82.43 C -108.631 82.697 -108.321 83.225 -107.942 83.636 C -107.641 83.962 -107.4 84.399 -107.03 84.595 C -105.628 85.341 -104.084 85.37 -102.512 85.333 C -102.5 85.154 -102.489 84.986 -102.477 84.806 C -100.894 85.3 -99.113 87.125 -99.116 88.458 C -101.68 88.458 -104.242 88.449 -106.804 88.473 C -107.077 88.475 -107.349 88.675 -107.622 88.784 C -107.363 88.935 -107.108 89.207 -106.844 89.217 Z" style="fill: rgb(255, 255, 255);"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4 KiB |
21
Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json
vendored
Normal file
21
Apple/App/Assets.xcassets/WireGuardTitle.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "WireGuardTitle.svg",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg
vendored
Normal file
3
Apple/App/Assets.xcassets/WireGuardTitle.imageset/WireGuardTitle.svg
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="1538" height="210" viewBox="0 0 1538 210" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M65.7132 204.476L0.500244 4.49222H24.9555L74.5901 158.827L125.493 4.49222H147.774L198.496 158.467L248.311 4.49222H272.041L207.009 204.476H188.895L136.182 43.4409L84.0113 204.476H65.7132ZM267.577 204.476V49.4109H292.213V204.476H267.577ZM349.324 136.908V204.476H325.051V50.1407H431.568C447.387 50.1407 459.766 53.9449 468.703 61.5533C477.638 69.1618 482.107 79.7288 482.107 93.2544C482.288 103.759 478.504 113.946 471.51 121.785C464.445 129.818 455.237 134.739 443.885 136.548L486.817 204.478H460.551L416.713 136.91L349.324 136.908ZM349.324 114.265H431.565C439.898 114.265 446.268 112.453 450.676 108.83C455.083 105.207 457.287 100.014 457.288 93.2518C457.288 86.49 455.084 81.3275 450.676 77.7643C446.266 74.2033 439.896 72.422 431.565 72.4202H349.324V114.265ZM504.99 204.476V49.7808H650.085V72.4241H529.259V111.008H608.602V133.289H529.259V182.198H656.785V204.48H504.98L504.99 204.476ZM827.46 150.318V120.972H772.934V97.9663H852.277V158.107C842.614 174.773 830.025 187.543 814.508 196.419C798.989 205.296 781.387 209.734 761.704 209.734C731.752 209.734 707.026 199.861 687.524 180.116C668.02 160.371 658.269 135.343 658.269 105.031C658.269 74.5986 668.051 49.5097 687.615 29.7643C707.179 10.0196 731.876 0.147217 761.704 0.147217C780.181 0.147217 797.028 4.19276 812.244 12.2839C827.443 20.3602 840.25 32.2924 849.379 46.8828L828.547 61.7363C822.413 49.9333 812.97 40.1755 801.375 33.6582C789.281 26.8286 775.592 23.3281 761.704 23.5135C739.242 23.5135 720.644 31.2122 705.911 46.6097C691.175 62.0066 683.808 81.4796 683.81 105.029C683.81 128.578 691.177 148.021 705.911 163.358C720.643 178.696 739.241 186.365 761.704 186.363C775.711 186.363 788.18 183.344 799.111 177.306C810.039 171.269 819.488 162.274 827.46 150.318ZM879.358 50.1436H903.631V149.231C903.631 164.085 907.134 174.108 914.138 179.301C921.141 184.494 935.029 187.091 955.802 187.09C976.693 187.09 990.642 184.494 997.647 179.301C1004.65 174.11 1008.15 164.087 1008.15 149.231V50.1436H1032.25V155.57C1032.25 174.531 1026.24 188.238 1014.22 196.691C1002.2 205.142 982.61 209.369 955.439 209.372C928.386 209.372 908.943 205.205 897.11 196.872C885.273 188.539 879.355 174.772 879.357 155.57L879.358 50.1436ZM1028.45 204.476L1107.07 49.7808H1122.29L1201.63 204.476H1175.73L1155.62 165.167H1073.92L1053.99 204.476H1028.45ZM1084.43 144.698H1144.93L1114.86 85.4626L1084.43 144.698ZM1228.55 136.908V204.476H1204.27V50.1406H1310.79C1326.61 50.1406 1338.99 53.9448 1347.93 61.5532C1356.86 69.1617 1361.33 79.7287 1361.33 93.2543C1361.51 103.759 1357.73 113.945 1350.73 121.784C1343.67 129.817 1334.46 134.739 1323.11 136.548L1366.04 204.478H1339.77L1295.94 136.91L1228.55 136.908ZM1228.55 114.265H1310.79C1319.12 114.265 1325.49 112.453 1329.9 108.83C1334.31 105.207 1336.51 100.014 1336.51 93.2517C1336.51 86.4899 1334.31 81.3274 1329.9 77.7642C1325.49 74.2032 1319.12 72.4218 1310.79 72.4201H1228.55V114.265ZM1453.24 49.7818C1478.48 49.7818 1498.83 56.9973 1514.29 71.4281C1529.74 85.861 1537.47 104.61 1537.47 127.674C1537.47 150.983 1529.9 169.611 1514.74 183.559C1499.58 197.506 1479.08 204.48 1453.24 204.481H1384.59V49.786H1453.24L1453.24 49.7818ZM1453.6 72.0631H1408.86V182.2H1453.6C1471.96 182.2 1486.39 177.278 1496.9 167.436C1507.4 157.595 1512.66 144.221 1512.66 127.312C1512.66 111.009 1507.22 97.7246 1496.35 87.4596C1485.48 77.1966 1471.23 72.0651 1453.6 72.0631Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,21 +1,13 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@main
|
#if !os(macOS)
|
||||||
@MainActor
|
@MainActor
|
||||||
|
@main
|
||||||
struct BurrowApp: App {
|
struct BurrowApp: App {
|
||||||
static let tunnel = Tunnel { manager, proto in
|
|
||||||
proto.serverAddress = "hackclub.com"
|
|
||||||
manager.localizedDescription = "Burrow"
|
|
||||||
}
|
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
@NSApplicationDelegateAdaptor(AppDelegate.self)
|
|
||||||
var delegate
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
TunnelView(tunnel: Self.tunnel)
|
BurrowView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
26
Apple/App/BurrowView.swift
Normal file
26
Apple/App/BurrowView.swift
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct BurrowView: View {
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
VStack {
|
||||||
|
NetworkCarouselView()
|
||||||
|
Spacer()
|
||||||
|
TunnelStatusView()
|
||||||
|
TunnelButton()
|
||||||
|
.padding(.bottom)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.navigationTitle("Networks")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
struct NetworkView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
BurrowView()
|
||||||
|
.environment(\.tunnel, PreviewTunnel())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
50
Apple/App/FloatingButtonStyle.swift
Normal file
50
Apple/App/FloatingButtonStyle.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct FloatingButtonStyle: ButtonStyle {
|
||||||
|
static let duration = 0.08
|
||||||
|
|
||||||
|
var color: Color
|
||||||
|
var cornerRadius: CGFloat
|
||||||
|
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
configuration.label
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(minHeight: 48)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
|
.fill(
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
configuration.isPressed ? color.opacity(0.9) : color.opacity(0.9),
|
||||||
|
configuration.isPressed ? color.opacity(0.9) : color
|
||||||
|
],
|
||||||
|
startPoint: .init(x: 0.2, y: 0),
|
||||||
|
endPoint: .init(x: 0.8, y: 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
|
.fill(configuration.isPressed ? .black : .white)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.shadow(color: .black.opacity(configuration.isPressed ? 0.0 : 0.1), radius: 2.5, x: 0, y: 2)
|
||||||
|
.scaleEffect(configuration.isPressed ? 0.975 : 1.0)
|
||||||
|
.padding(.bottom, 2)
|
||||||
|
.animation(
|
||||||
|
configuration.isPressed ? .easeOut(duration: Self.duration) : .easeIn(duration: Self.duration),
|
||||||
|
value: configuration.isPressed
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ButtonStyle where Self == FloatingButtonStyle {
|
||||||
|
static var floating: FloatingButtonStyle {
|
||||||
|
floating()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func floating(color: Color = .accentColor, cornerRadius: CGFloat = 10) -> FloatingButtonStyle {
|
||||||
|
FloatingButtonStyle(color: color, cornerRadius: cornerRadius)
|
||||||
|
}
|
||||||
|
}
|
||||||
679
Apple/App/MainMenu.xib
Normal file
679
Apple/App/MainMenu.xib
Normal file
|
|
@ -0,0 +1,679 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22689"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="BurrowApp" customModuleProvider="target"/>
|
||||||
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
|
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Burrow" id="1Xt-HY-uBw">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<menu key="submenu" title="Burrow" systemMenu="apple" id="uQy-DD-JDr">
|
||||||
|
<items>
|
||||||
|
<menuItem title="About Burrow" id="5kV-Vb-QxS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="orderFrontStandardAboutPanel:" target="-1" 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 Burrow" keyEquivalent="h" id="Olw-nP-bQN">
|
||||||
|
<connections>
|
||||||
|
<action selector="hide:" target="-1" 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="-1" id="VT4-aY-XCT"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Show All" id="Kd2-mp-pUS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
|
||||||
|
<menuItem title="Quit Burrow" keyEquivalent="q" id="4sb-4s-VLi">
|
||||||
|
<connections>
|
||||||
|
<action selector="terminate:" target="-1" 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="-1" id="4Si-XN-c54"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
|
||||||
|
<connections>
|
||||||
|
<action selector="openDocument:" target="-1" 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="-1" 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="-1" id="HmO-Ls-i7Q"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
|
||||||
|
<connections>
|
||||||
|
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
|
||||||
|
<connections>
|
||||||
|
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
|
||||||
|
<connections>
|
||||||
|
<action selector="revertDocumentToSaved:" target="-1" 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="-1" id="Din-rz-gC5"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
|
||||||
|
<connections>
|
||||||
|
<action selector="print:" target="-1" 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="-1" id="M6e-cu-g7V"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
|
||||||
|
<connections>
|
||||||
|
<action selector="redo:" target="-1" 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="-1" id="YJe-68-I9s"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
|
||||||
|
<connections>
|
||||||
|
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
|
||||||
|
<connections>
|
||||||
|
<action selector="paste:" target="-1" 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="-1" id="cEh-KX-wJQ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Delete" id="pa3-QI-u2k">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
|
||||||
|
<connections>
|
||||||
|
<action selector="selectAll:" target="-1" 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="-1" 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="-1" id="WD3-Gg-5AJ"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
|
||||||
|
<connections>
|
||||||
|
<action selector="performFindPanelAction:" target="-1" 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="-1" id="U76-nv-p5D"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
|
||||||
|
<connections>
|
||||||
|
<action selector="centerSelectionInVisibleArea:" target="-1" 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="-1" id="vFj-Ks-hy3"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
|
||||||
|
<connections>
|
||||||
|
<action selector="checkSpelling:" target="-1" 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="-1" id="7w6-Qz-0kB"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticSpellingCorrection:" target="-1" 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="-1" 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="-1" id="3IJ-Se-DZD"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Smart Links" id="cwL-P1-jid">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Data Detectors" id="tRr-pd-1PS">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggleAutomaticTextReplacement:" target="-1" 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="-1" id="sPh-Tk-edu"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="capitalizeWord:" target="-1" 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="-1" id="654-Ng-kyl"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="stopSpeaking:" target="-1" 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="-1" 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="-1" id="6dk-9l-Ckg"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use None" id="cDB-IK-hbR">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Tighten" id="46P-cB-AYj">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Loosen" id="ogc-rX-tC1">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="loosenKerning:" target="-1" 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="-1" id="7uR-wd-Dx6"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use None" id="J7y-lM-qPV">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Use All" id="xQD-1f-W4t">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="useAllLigatures:" target="-1" 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="-1" id="0vZ-95-Ywn"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Superscript" id="Rqc-34-cIF">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Subscript" id="I0S-gh-46l">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Raise" id="2h7-ER-AoG">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Lower" id="1tx-W0-xDw">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="lowerBaseline:" target="-1" 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="-1" 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="-1" 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="-1" 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="-1" id="zUv-R1-uAa"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
|
||||||
|
<connections>
|
||||||
|
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Justify" id="J5U-5w-g23">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
|
||||||
|
<connections>
|
||||||
|
<action selector="alignRight:" target="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" 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="-1" id="BXY-wc-z0C"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="runToolbarCustomizationPalette:" target="-1" 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="-1" 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="-1" 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="-1" id="VwT-WD-YPe"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Zoom" id="R4o-n2-Eq4">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="performZoom:" target="-1" 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="-1" 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="Burrow Help" keyEquivalent="?" id="FKE-Sm-Kum">
|
||||||
|
<connections>
|
||||||
|
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</menuItem>
|
||||||
|
</items>
|
||||||
|
<point key="canvasLocation" x="200" y="121"/>
|
||||||
|
</menu>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
//
|
|
||||||
// MenuView.swift
|
|
||||||
// App
|
|
||||||
//
|
|
||||||
// Created by Thomas Stubblefield on 5/13/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct MenuItemToggleView: View {
|
|
||||||
var tunnel: Tunnel
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack {
|
|
||||||
Text("Burrow")
|
|
||||||
.font(.headline)
|
|
||||||
Spacer()
|
|
||||||
Toggle("Burrow", isOn: tunnel.isOn)
|
|
||||||
.labelsHidden()
|
|
||||||
.disabled(tunnel.isDisabled)
|
|
||||||
.toggleStyle(.switch)
|
|
||||||
}
|
|
||||||
.padding(.horizontal, 4)
|
|
||||||
.padding(10)
|
|
||||||
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Tunnel {
|
|
||||||
var isDisabled: Bool {
|
|
||||||
switch self.status {
|
|
||||||
case .disconnected, .permissionRequired, .connected:
|
|
||||||
return false
|
|
||||||
case .unknown, .disabled, .connecting, .reasserting, .disconnecting, .invalid, .configurationReadWriteFailed:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isOn: Binding<Bool> {
|
|
||||||
Binding {
|
|
||||||
switch self.status {
|
|
||||||
case .connecting, .reasserting, .connected:
|
|
||||||
true
|
|
||||||
default:
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} set: { newValue in
|
|
||||||
switch (self.status, newValue) {
|
|
||||||
case (.permissionRequired, true):
|
|
||||||
Task { try await self.configure() }
|
|
||||||
case (.disconnected, true):
|
|
||||||
try? self.start()
|
|
||||||
case (.connected, false):
|
|
||||||
self.stop()
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
64
Apple/App/MenuItemToggleView.swift
Normal file
64
Apple/App/MenuItemToggleView.swift
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
//
|
||||||
|
// MenuItemToggleView.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by Thomas Stubblefield on 5/13/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MenuItemToggleView: View {
|
||||||
|
@Environment(\.tunnel)
|
||||||
|
var tunnel: Tunnel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("Burrow")
|
||||||
|
.font(.headline)
|
||||||
|
Text(tunnel.status.description)
|
||||||
|
.font(.subheadline)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
Toggle(isOn: tunnel.toggleIsOn) {
|
||||||
|
}
|
||||||
|
.disabled(tunnel.toggleDisabled)
|
||||||
|
.toggleStyle(.switch)
|
||||||
|
}
|
||||||
|
.accessibilityElement(children: .combine)
|
||||||
|
.padding(.horizontal, 4)
|
||||||
|
.padding(10)
|
||||||
|
.frame(minWidth: 300, minHeight: 32, maxHeight: 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Tunnel {
|
||||||
|
fileprivate var toggleDisabled: Bool {
|
||||||
|
switch status {
|
||||||
|
case .disconnected, .permissionRequired, .connected, .disconnecting:
|
||||||
|
false
|
||||||
|
case .unknown, .disabled, .connecting, .reasserting, .invalid, .configurationReadWriteFailed:
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toggleIsOn: Binding<Bool> {
|
||||||
|
Binding {
|
||||||
|
switch status {
|
||||||
|
case .connecting, .reasserting, .connected:
|
||||||
|
true
|
||||||
|
default:
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} set: { newValue in
|
||||||
|
switch (status, newValue) {
|
||||||
|
case (.permissionRequired, true):
|
||||||
|
enable()
|
||||||
|
case (_, true):
|
||||||
|
start()
|
||||||
|
case (_, false):
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
167
Apple/App/NetworkExtensionTunnel.swift
Normal file
167
Apple/App/NetworkExtensionTunnel.swift
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
import BurrowShared
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
@Observable
|
||||||
|
class NetworkExtensionTunnel: Tunnel {
|
||||||
|
@MainActor private(set) var status: TunnelStatus = .unknown
|
||||||
|
private var error: NEVPNError?
|
||||||
|
|
||||||
|
private let logger = Logger.logger(for: Tunnel.self)
|
||||||
|
private let bundleIdentifier: String
|
||||||
|
private var tasks: [Task<Void, Error>] = []
|
||||||
|
|
||||||
|
// Each manager corresponds to one entry in the Settings app.
|
||||||
|
// Our goal is to maintain a single manager, so we create one if none exist and delete any extra.
|
||||||
|
private var managers: [NEVPNManager]? {
|
||||||
|
didSet { Task { await updateStatus() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private var currentStatus: TunnelStatus {
|
||||||
|
guard let managers = managers else {
|
||||||
|
guard let error = error else {
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
switch error.code {
|
||||||
|
case .configurationReadWriteFailed:
|
||||||
|
return .configurationReadWriteFailed
|
||||||
|
default:
|
||||||
|
return .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let manager = managers.first else {
|
||||||
|
return .permissionRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
guard manager.isEnabled else {
|
||||||
|
return .disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager.connection.tunnelStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init() {
|
||||||
|
self.init(Constants.networkExtensionBundleIdentifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(_ bundleIdentifier: String) {
|
||||||
|
self.bundleIdentifier = bundleIdentifier
|
||||||
|
|
||||||
|
let center = NotificationCenter.default
|
||||||
|
let configurationChanged = Task { [weak self] in
|
||||||
|
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
|
||||||
|
await self?.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let statusChanged = Task { [weak self] in
|
||||||
|
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
|
||||||
|
await self?.updateStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks = [configurationChanged, statusChanged]
|
||||||
|
|
||||||
|
Task { await update() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func update() async {
|
||||||
|
do {
|
||||||
|
managers = try await NETunnelProviderManager.managers
|
||||||
|
await self.updateStatus()
|
||||||
|
} catch let vpnError as NEVPNError {
|
||||||
|
error = vpnError
|
||||||
|
} catch {
|
||||||
|
logger.error("Failed to update VPN configurations: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateStatus() async {
|
||||||
|
await MainActor.run {
|
||||||
|
status = currentStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure() async throws {
|
||||||
|
if managers == nil {
|
||||||
|
await update()
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let managers = managers else { return }
|
||||||
|
|
||||||
|
if managers.count > 1 {
|
||||||
|
try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in
|
||||||
|
for manager in managers.suffix(from: 1) {
|
||||||
|
group.addTask { try await manager.remove() }
|
||||||
|
}
|
||||||
|
try await group.waitForAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard managers.isEmpty else { return }
|
||||||
|
|
||||||
|
let manager = NETunnelProviderManager()
|
||||||
|
manager.localizedDescription = "Burrow"
|
||||||
|
|
||||||
|
let proto = NETunnelProviderProtocol()
|
||||||
|
proto.providerBundleIdentifier = bundleIdentifier
|
||||||
|
proto.serverAddress = "hackclub.com"
|
||||||
|
|
||||||
|
manager.protocolConfiguration = proto
|
||||||
|
try await manager.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
func start() {
|
||||||
|
guard let manager = managers?.first else { return }
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
if !manager.isEnabled {
|
||||||
|
manager.isEnabled = true
|
||||||
|
try await manager.save()
|
||||||
|
}
|
||||||
|
try manager.connection.startVPNTunnel()
|
||||||
|
} catch {
|
||||||
|
logger.error("Failed to start: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
guard let manager = managers?.first else { return }
|
||||||
|
manager.connection.stopVPNTunnel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func enable() {
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await configure()
|
||||||
|
} catch {
|
||||||
|
logger.error("Failed to enable: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
tasks.forEach { $0.cancel() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NEVPNConnection {
|
||||||
|
fileprivate var tunnelStatus: TunnelStatus {
|
||||||
|
switch status {
|
||||||
|
case .connected:
|
||||||
|
.connected(connectedDate!)
|
||||||
|
case .connecting:
|
||||||
|
.connecting
|
||||||
|
case .disconnecting:
|
||||||
|
.disconnecting
|
||||||
|
case .disconnected:
|
||||||
|
.disconnected
|
||||||
|
case .reasserting:
|
||||||
|
.reasserting
|
||||||
|
case .invalid:
|
||||||
|
.invalid
|
||||||
|
@unknown default:
|
||||||
|
.unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
Apple/App/NetworkView.swift
Normal file
88
Apple/App/NetworkView.swift
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct NetworkView<Content: View>: View {
|
||||||
|
var color: Color
|
||||||
|
var content: () -> Content
|
||||||
|
|
||||||
|
private var gradient: LinearGradient {
|
||||||
|
LinearGradient(
|
||||||
|
colors: [
|
||||||
|
color.opacity(0.8),
|
||||||
|
color
|
||||||
|
],
|
||||||
|
startPoint: .init(x: 0.2, y: 0),
|
||||||
|
endPoint: .init(x: 0.8, y: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
content()
|
||||||
|
.frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.fill(gradient)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.fill(.white)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.shadow(color: .black.opacity(0.1), radius: 3.0, x: 0, y: 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AddNetworkView: View {
|
||||||
|
var body: some View {
|
||||||
|
Text("Add Network")
|
||||||
|
.frame(maxWidth: .infinity, minHeight: 175, maxHeight: 175)
|
||||||
|
.background(
|
||||||
|
RoundedRectangle(cornerRadius: 10)
|
||||||
|
.stroke(style: .init(lineWidth: 2, dash: [6]))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NetworkView where Content == AnyView {
|
||||||
|
init(network: any Network) {
|
||||||
|
color = network.backgroundColor
|
||||||
|
content = { AnyView(network.label) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NetworkCarouselView: View {
|
||||||
|
var networks: [any Network] = [
|
||||||
|
HackClub(id: "1"),
|
||||||
|
HackClub(id: "2"),
|
||||||
|
WireGuard(id: "4"),
|
||||||
|
HackClub(id: "5"),
|
||||||
|
]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
LazyHStack {
|
||||||
|
ForEach(networks, id: \.id) { network in
|
||||||
|
NetworkView(network: network)
|
||||||
|
.containerRelativeFrame(.horizontal, count: 10, span: 7, spacing: 0, alignment: .center)
|
||||||
|
.scrollTransition(.interactive, axis: .horizontal) { content, phase in
|
||||||
|
content
|
||||||
|
.scaleEffect(1.0 - abs(phase.value) * 0.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddNetworkView()
|
||||||
|
}
|
||||||
|
.scrollTargetLayout()
|
||||||
|
}
|
||||||
|
.scrollClipDisabled()
|
||||||
|
.scrollIndicators(.hidden)
|
||||||
|
.defaultScrollAnchor(.center)
|
||||||
|
.scrollTargetBehavior(.viewAligned)
|
||||||
|
.containerRelativeFrame(.horizontal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
struct NetworkCarouselView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
NetworkCarouselView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
23
Apple/App/Networks/HackClub.swift
Normal file
23
Apple/App/Networks/HackClub.swift
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct HackClub: Network {
|
||||||
|
var id: String
|
||||||
|
var backgroundColor: Color { .init("HackClub") }
|
||||||
|
|
||||||
|
var label: some View {
|
||||||
|
GeometryReader { reader in
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Image("HackClub")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(height: reader.size.height / 4)
|
||||||
|
Spacer()
|
||||||
|
Text("@conradev")
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.font(.body.monospaced())
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Apple/App/Networks/Network.swift
Normal file
10
Apple/App/Networks/Network.swift
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
protocol Network {
|
||||||
|
associatedtype Label: View
|
||||||
|
|
||||||
|
var id: String { get }
|
||||||
|
var backgroundColor: Color { get }
|
||||||
|
|
||||||
|
var label: Label { get }
|
||||||
|
}
|
||||||
30
Apple/App/Networks/WireGuard.swift
Normal file
30
Apple/App/Networks/WireGuard.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct WireGuard: Network {
|
||||||
|
var id: String
|
||||||
|
var backgroundColor: Color { .init("WireGuard") }
|
||||||
|
|
||||||
|
var label: some View {
|
||||||
|
GeometryReader { reader in
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Image("WireGuard")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
Image("WireGuardTitle")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: reader.size.width / 2)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: reader.size.height / 4)
|
||||||
|
Spacer()
|
||||||
|
Text("@conradev")
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.font(.body.monospaced())
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
import Foundation
|
|
||||||
import NetworkExtension
|
|
||||||
|
|
||||||
extension Tunnel {
|
|
||||||
enum Status: CustomStringConvertible, Equatable, Hashable {
|
|
||||||
case unknown
|
|
||||||
case permissionRequired
|
|
||||||
case disabled
|
|
||||||
case connecting
|
|
||||||
case connected(Date)
|
|
||||||
case disconnecting
|
|
||||||
case disconnected
|
|
||||||
case reasserting
|
|
||||||
case invalid
|
|
||||||
case configurationReadWriteFailed
|
|
||||||
|
|
||||||
var description: String {
|
|
||||||
switch self {
|
|
||||||
case .unknown:
|
|
||||||
return "Unknown"
|
|
||||||
case .permissionRequired:
|
|
||||||
return "Permission Required"
|
|
||||||
case .disconnected:
|
|
||||||
return "Disconnected"
|
|
||||||
case .disabled:
|
|
||||||
return "Disabled"
|
|
||||||
case .connecting:
|
|
||||||
return "Connecting"
|
|
||||||
case .connected:
|
|
||||||
return "Connected"
|
|
||||||
case .disconnecting:
|
|
||||||
return "Disconnecting"
|
|
||||||
case .reasserting:
|
|
||||||
return "Reasserting"
|
|
||||||
case .invalid:
|
|
||||||
return "Invalid"
|
|
||||||
case .configurationReadWriteFailed:
|
|
||||||
return "System Error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,146 +1,50 @@
|
||||||
import BurrowShared
|
|
||||||
import NetworkExtension
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
protocol Tunnel {
|
||||||
|
var status: TunnelStatus { get }
|
||||||
|
|
||||||
|
func start()
|
||||||
|
func stop()
|
||||||
|
func enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TunnelStatus: Equatable, Hashable {
|
||||||
|
case unknown
|
||||||
|
case permissionRequired
|
||||||
|
case disabled
|
||||||
|
case connecting
|
||||||
|
case connected(Date)
|
||||||
|
case disconnecting
|
||||||
|
case disconnected
|
||||||
|
case reasserting
|
||||||
|
case invalid
|
||||||
|
case configurationReadWriteFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TunnelKey: EnvironmentKey {
|
||||||
|
static let defaultValue: any Tunnel = NetworkExtensionTunnel()
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
var tunnel: any Tunnel {
|
||||||
|
get { self[TunnelKey.self] }
|
||||||
|
set { self[TunnelKey.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
@Observable
|
@Observable
|
||||||
class Tunnel {
|
class PreviewTunnel: Tunnel {
|
||||||
private(set) var status: Status = .unknown
|
var status: TunnelStatus = .permissionRequired
|
||||||
private var error: NEVPNError?
|
|
||||||
|
|
||||||
private let logger = Logger.logger(for: Tunnel.self)
|
func start() {
|
||||||
private let bundleIdentifier: String
|
status = .connected(.now)
|
||||||
private let configure: (NETunnelProviderManager, NETunnelProviderProtocol) -> Void
|
|
||||||
private var tasks: [Task<Void, Error>] = []
|
|
||||||
|
|
||||||
// Each manager corresponds to one entry in the Settings app.
|
|
||||||
// Our goal is to maintain a single manager, so we create one if none exist and delete extra if there are any.
|
|
||||||
private var managers: [NEVPNManager]? {
|
|
||||||
didSet { status = currentStatus }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentStatus: Status {
|
|
||||||
guard let managers = managers else {
|
|
||||||
guard let error = error else {
|
|
||||||
return .unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
switch error.code {
|
|
||||||
case .configurationReadWriteFailed:
|
|
||||||
return .configurationReadWriteFailed
|
|
||||||
default:
|
|
||||||
return .unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let manager = managers.first else {
|
|
||||||
return .permissionRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
guard manager.isEnabled else {
|
|
||||||
return .disabled
|
|
||||||
}
|
|
||||||
|
|
||||||
return manager.connection.tunnelStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
convenience init(configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
|
|
||||||
self.init("com.hackclub.burrow.network", configure: configure)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(_ bundleIdentifier: String, configure: @escaping (NETunnelProviderManager, NETunnelProviderProtocol) -> Void) {
|
|
||||||
self.bundleIdentifier = bundleIdentifier
|
|
||||||
self.configure = configure
|
|
||||||
|
|
||||||
let center = NotificationCenter.default
|
|
||||||
let configurationChanged = Task {
|
|
||||||
for try await _ in center.notifications(named: .NEVPNConfigurationChange).map({ _ in () }) {
|
|
||||||
await update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let statusChanged = Task {
|
|
||||||
for try await _ in center.notifications(named: .NEVPNStatusDidChange).map({ _ in () }) {
|
|
||||||
await MainActor.run {
|
|
||||||
status = currentStatus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tasks = [configurationChanged, statusChanged]
|
|
||||||
|
|
||||||
Task { await update() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private func update() async {
|
|
||||||
do {
|
|
||||||
let updated = try await NETunnelProviderManager.managers
|
|
||||||
await MainActor.run {
|
|
||||||
managers = updated
|
|
||||||
}
|
|
||||||
} catch let vpnError as NEVPNError {
|
|
||||||
error = vpnError
|
|
||||||
} catch {
|
|
||||||
logger.error("Failed to update VPN configurations: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configure() async throws {
|
|
||||||
if managers == nil {
|
|
||||||
await update()
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let managers = managers else { return }
|
|
||||||
|
|
||||||
if managers.count > 1 {
|
|
||||||
try await withThrowingTaskGroup(of: Void.self, returning: Void.self) { group in
|
|
||||||
for manager in managers.suffix(from: 1) {
|
|
||||||
group.addTask { try await manager.remove() }
|
|
||||||
}
|
|
||||||
try await group.waitForAll()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if managers.isEmpty {
|
|
||||||
let manager = NETunnelProviderManager()
|
|
||||||
let proto = NETunnelProviderProtocol()
|
|
||||||
proto.providerBundleIdentifier = bundleIdentifier
|
|
||||||
configure(manager, proto)
|
|
||||||
|
|
||||||
manager.protocolConfiguration = proto
|
|
||||||
try await manager.save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func start() throws {
|
|
||||||
guard let manager = managers?.first else { return }
|
|
||||||
try manager.connection.startVPNTunnel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
guard let manager = managers?.first else { return }
|
status = .disconnected
|
||||||
manager.connection.stopVPNTunnel()
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
tasks.forEach { $0.cancel() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NEVPNConnection {
|
|
||||||
var tunnelStatus: Tunnel.Status {
|
|
||||||
switch status {
|
|
||||||
case .connected:
|
|
||||||
.connected(connectedDate!)
|
|
||||||
case .connecting:
|
|
||||||
.connecting
|
|
||||||
case .disconnecting:
|
|
||||||
.disconnecting
|
|
||||||
case .disconnected:
|
|
||||||
.disconnected
|
|
||||||
case .reasserting:
|
|
||||||
.reasserting
|
|
||||||
case .invalid:
|
|
||||||
.invalid
|
|
||||||
@unknown default:
|
|
||||||
.unknown
|
|
||||||
}
|
}
|
||||||
|
func enable() {
|
||||||
|
status = .disconnected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
||||||
61
Apple/App/TunnelButton.swift
Normal file
61
Apple/App/TunnelButton.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TunnelButton: View {
|
||||||
|
@Environment(\.tunnel)
|
||||||
|
var tunnel: any Tunnel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if let action = tunnel.action {
|
||||||
|
Button {
|
||||||
|
tunnel.perform(action)
|
||||||
|
} label: {
|
||||||
|
Text(action.description)
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
.buttonStyle(.floating)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Tunnel {
|
||||||
|
fileprivate var action: TunnelButton.Action? {
|
||||||
|
switch status {
|
||||||
|
case .permissionRequired, .invalid:
|
||||||
|
.enable
|
||||||
|
case .disabled, .disconnecting, .disconnected:
|
||||||
|
.start
|
||||||
|
case .connecting, .connected, .reasserting:
|
||||||
|
.stop
|
||||||
|
case .unknown, .configurationReadWriteFailed:
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TunnelButton {
|
||||||
|
fileprivate enum Action {
|
||||||
|
case enable
|
||||||
|
case start
|
||||||
|
case stop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TunnelButton.Action {
|
||||||
|
var description: LocalizedStringKey {
|
||||||
|
switch self {
|
||||||
|
case .enable: "Enable"
|
||||||
|
case .start: "Start"
|
||||||
|
case .stop: "Stop"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Tunnel {
|
||||||
|
fileprivate func perform(_ action: TunnelButton.Action) {
|
||||||
|
switch action {
|
||||||
|
case .enable: enable()
|
||||||
|
case .start: start()
|
||||||
|
case .stop: stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Apple/App/TunnelStatusView.swift
Normal file
37
Apple/App/TunnelStatusView.swift
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TunnelStatusView: View {
|
||||||
|
@Environment(\.tunnel)
|
||||||
|
var tunnel: any Tunnel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(tunnel.status.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TunnelStatus: CustomStringConvertible {
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .unknown:
|
||||||
|
"Unknown"
|
||||||
|
case .permissionRequired:
|
||||||
|
"Permission Required"
|
||||||
|
case .disconnected:
|
||||||
|
"Disconnected"
|
||||||
|
case .disabled:
|
||||||
|
"Disabled"
|
||||||
|
case .connecting:
|
||||||
|
"Connecting…"
|
||||||
|
case .connected:
|
||||||
|
"Connected"
|
||||||
|
case .disconnecting:
|
||||||
|
"Disconnecting…"
|
||||||
|
case .reasserting:
|
||||||
|
"Reasserting…"
|
||||||
|
case .invalid:
|
||||||
|
"Invalid"
|
||||||
|
case .configurationReadWriteFailed:
|
||||||
|
"System Error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct TunnelView: View {
|
|
||||||
var tunnel: Tunnel
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack {
|
|
||||||
Text(verbatim: tunnel.status.description)
|
|
||||||
switch tunnel.status {
|
|
||||||
case .connected:
|
|
||||||
Button("Disconnect", action: stop)
|
|
||||||
case .permissionRequired:
|
|
||||||
Button("Allow", action: configure)
|
|
||||||
case .disconnected:
|
|
||||||
Button("Start", action: start)
|
|
||||||
default:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func start() {
|
|
||||||
try? tunnel.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func stop() {
|
|
||||||
tunnel.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func configure() {
|
|
||||||
Task { try await tunnel.configure() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,24 +9,32 @@
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
|
0B28F1562ABF463A000D44B0 /* DataTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B28F1552ABF463A000D44B0 /* DataTypes.swift */; };
|
||||||
0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; };
|
0B46E8E02AC918CA00BA2A3C /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B46E8DF2AC918CA00BA2A3C /* Client.swift */; };
|
||||||
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuView.swift */; };
|
43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */; };
|
||||||
D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; };
|
D00117312B2FFFC900D87C25 /* NWConnection+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */; };
|
||||||
D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; };
|
D00117332B3001A400D87C25 /* NewlineProtocolFramer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */; };
|
||||||
D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; };
|
D001173B2B30341C00D87C25 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001173A2B30341C00D87C25 /* Logging.swift */; };
|
||||||
D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
D00117442B30372900D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
||||||
D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
D00117452B30372C00D87C25 /* libBurrowShared.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D00117382B30341C00D87C25 /* libBurrowShared.a */; };
|
||||||
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
|
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00AA8962A4669BC005C8102 /* AppDelegate.swift */; };
|
||||||
|
D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01A79302B81630D0024EC91 /* NetworkView.swift */; };
|
||||||
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
|
D020F65829E4A697002790F6 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D020F65729E4A697002790F6 /* PacketTunnelProvider.swift */; };
|
||||||
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
D020F65D29E4A697002790F6 /* BurrowNetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D020F65329E4A697002790F6 /* BurrowNetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6512B8A79C20006B8AD /* HackClub.swift */; };
|
||||||
|
D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = D032E6532B8A79DA0006B8AD /* WireGuard.swift */; };
|
||||||
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; };
|
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */; };
|
||||||
D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */; };
|
D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */; };
|
||||||
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; };
|
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D05B9F7929E39EED008CB1F9 /* Assets.xcassets */; };
|
||||||
|
D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */; };
|
||||||
D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; };
|
D08252762B5C9FC4005DA378 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08252752B5C9FC4005DA378 /* Constants.swift */; };
|
||||||
|
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D09150412B9D2AF700BE3CB0 /* MainMenu.xib */; };
|
||||||
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; };
|
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */; };
|
||||||
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BCC5FE2A086E1C00AD070D /* Status.swift */; };
|
|
||||||
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; };
|
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B98FC629FDC5B5004E7149 /* Tunnel.swift */; };
|
||||||
D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; };
|
D0BCC6092A09A03E00AD070D /* libburrow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCC6032A09535900AD070D /* libburrow.a */; };
|
||||||
D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; };
|
D0BCC60A2A09A0B800AD070D /* build-rust.sh in Resources */ = {isa = PBXBuildFile; fileRef = D0B98FBF29FD8072004E7149 /* build-rust.sh */; };
|
||||||
|
D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */; };
|
||||||
|
D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5952B818B2900F6A84B /* TunnelButton.swift */; };
|
||||||
|
D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */; };
|
||||||
|
D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FAB5992B818B9600F6A84B /* Network.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
|
@ -70,7 +78,7 @@
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = "<group>"; };
|
0B28F1552ABF463A000D44B0 /* DataTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTypes.swift; sourceTree = "<group>"; };
|
||||||
0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
|
0B46E8DF2AC918CA00BA2A3C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
|
||||||
43AA26D72A10004900F14CE6 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
|
43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemToggleView.swift; sourceTree = "<group>"; };
|
||||||
D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = "<group>"; };
|
D00117302B2FFFC900D87C25 /* NWConnection+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NWConnection+Async.swift"; sourceTree = "<group>"; };
|
||||||
D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = "<group>"; };
|
D00117322B3001A400D87C25 /* NewlineProtocolFramer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewlineProtocolFramer.swift; sourceTree = "<group>"; };
|
||||||
D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
D00117382B30341C00D87C25 /* libBurrowShared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBurrowShared.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
|
@ -78,6 +86,7 @@
|
||||||
D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
D00117412B30347800D87C25 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
||||||
D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
D00117422B30348D00D87C25 /* Shared.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
||||||
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
D00AA8962A4669BC005C8102 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
D01A79302B81630D0024EC91 /* NetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkView.swift; sourceTree = "<group>"; };
|
||||||
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
|
D020F63D29E4A1FF002790F6 /* Identity.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Identity.xcconfig; sourceTree = "<group>"; };
|
||||||
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; };
|
D020F64029E4A1FF002790F6 /* Compiler.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Compiler.xcconfig; sourceTree = "<group>"; };
|
||||||
D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
D020F64229E4A1FF002790F6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
|
@ -92,19 +101,26 @@
|
||||||
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; };
|
D020F66729E4A95D002790F6 /* NetworkExtension-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetworkExtension-iOS.entitlements"; sourceTree = "<group>"; };
|
||||||
D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; };
|
D020F66829E4AA74002790F6 /* App-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-iOS.entitlements"; sourceTree = "<group>"; };
|
||||||
D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; };
|
D020F66929E4AA74002790F6 /* App-macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "App-macOS.entitlements"; sourceTree = "<group>"; };
|
||||||
|
D032E6512B8A79C20006B8AD /* HackClub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HackClub.swift; sourceTree = "<group>"; };
|
||||||
|
D032E6532B8A79DA0006B8AD /* WireGuard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WireGuard.swift; sourceTree = "<group>"; };
|
||||||
D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
D05B9F7229E39EEC008CB1F9 /* Burrow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Burrow.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; };
|
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowApp.swift; sourceTree = "<group>"; };
|
||||||
D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelView.swift; sourceTree = "<group>"; };
|
D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BurrowView.swift; sourceTree = "<group>"; };
|
||||||
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
|
D08252742B5C9DEB005DA378 /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
|
||||||
D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
D08252752B5C9FC4005DA378 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
|
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||||
D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; };
|
D0B98FBF29FD8072004E7149 /* build-rust.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-rust.sh"; sourceTree = "<group>"; };
|
||||||
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; };
|
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = "<group>"; };
|
||||||
D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; };
|
D0B98FD829FDDB6F004E7149 /* libburrow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libburrow.h; sourceTree = "<group>"; };
|
||||||
D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
D0B98FDC29FDDDCF004E7149 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
||||||
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; };
|
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NetworkExtension+Async.swift"; sourceTree = "<group>"; };
|
||||||
D0BCC5FE2A086E1C00AD070D /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
|
||||||
D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
D0BCC6032A09535900AD070D /* libburrow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libburrow.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkExtensionTunnel.swift; sourceTree = "<group>"; };
|
||||||
|
D0FAB5952B818B2900F6A84B /* TunnelButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelButton.swift; sourceTree = "<group>"; };
|
||||||
|
D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusView.swift; sourceTree = "<group>"; };
|
||||||
|
D0FAB5992B818B9600F6A84B /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
|
@ -135,14 +151,6 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
43AA26D62A0FFFD000F14CE6 /* Menu */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
43AA26D72A10004900F14CE6 /* MenuView.swift */,
|
|
||||||
);
|
|
||||||
path = Menu;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
D00117392B30341C00D87C25 /* Shared */ = {
|
D00117392B30341C00D87C25 /* Shared */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -199,6 +207,16 @@
|
||||||
path = NetworkExtension;
|
path = NetworkExtension;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D032E64D2B8A69C90006B8AD /* Networks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D0FAB5992B818B9600F6A84B /* Network.swift */,
|
||||||
|
D032E6512B8A79C20006B8AD /* HackClub.swift */,
|
||||||
|
D032E6532B8A79DA0006B8AD /* WireGuard.swift */,
|
||||||
|
);
|
||||||
|
path = Networks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D05B9F6929E39EEC008CB1F9 = {
|
D05B9F6929E39EEC008CB1F9 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
|
@ -224,14 +242,20 @@
|
||||||
D05B9F7429E39EEC008CB1F9 /* App */ = {
|
D05B9F7429E39EEC008CB1F9 /* App */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
43AA26D62A0FFFD000F14CE6 /* Menu */,
|
|
||||||
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */,
|
D05B9F7529E39EEC008CB1F9 /* BurrowApp.swift */,
|
||||||
D00AA8962A4669BC005C8102 /* AppDelegate.swift */,
|
D00AA8962A4669BC005C8102 /* AppDelegate.swift */,
|
||||||
D05B9F7729E39EEC008CB1F9 /* TunnelView.swift */,
|
43AA26D72A10004900F14CE6 /* MenuItemToggleView.swift */,
|
||||||
|
D05B9F7729E39EEC008CB1F9 /* BurrowView.swift */,
|
||||||
|
D01A79302B81630D0024EC91 /* NetworkView.swift */,
|
||||||
|
D032E64D2B8A69C90006B8AD /* Networks */,
|
||||||
|
D0FAB5972B818B8200F6A84B /* TunnelStatusView.swift */,
|
||||||
|
D0FAB5952B818B2900F6A84B /* TunnelButton.swift */,
|
||||||
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */,
|
D0B98FC629FDC5B5004E7149 /* Tunnel.swift */,
|
||||||
D0BCC5FE2A086E1C00AD070D /* Status.swift */,
|
D0FAB5912B818A5900F6A84B /* NetworkExtensionTunnel.swift */,
|
||||||
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */,
|
D0BCC5FC2A086D4700AD070D /* NetworkExtension+Async.swift */,
|
||||||
|
D05EF8C72B81818D0017AB4F /* FloatingButtonStyle.swift */,
|
||||||
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */,
|
D05B9F7929E39EED008CB1F9 /* Assets.xcassets */,
|
||||||
|
D09150412B9D2AF700BE3CB0 /* MainMenu.xib */,
|
||||||
D020F66829E4AA74002790F6 /* App-iOS.entitlements */,
|
D020F66829E4AA74002790F6 /* App-iOS.entitlements */,
|
||||||
D020F66929E4AA74002790F6 /* App-macOS.entitlements */,
|
D020F66929E4AA74002790F6 /* App-macOS.entitlements */,
|
||||||
D020F64929E4A34B002790F6 /* App.xcconfig */,
|
D020F64929E4A34B002790F6 /* App.xcconfig */,
|
||||||
|
|
@ -319,7 +343,7 @@
|
||||||
attributes = {
|
attributes = {
|
||||||
BuildIndependentTargetsInParallel = 1;
|
BuildIndependentTargetsInParallel = 1;
|
||||||
LastSwiftUpdateCheck = 1510;
|
LastSwiftUpdateCheck = 1510;
|
||||||
LastUpgradeCheck = 1430;
|
LastUpgradeCheck = 1520;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
D00117372B30341C00D87C25 = {
|
D00117372B30341C00D87C25 = {
|
||||||
CreatedOnToolsVersion = 15.1;
|
CreatedOnToolsVersion = 15.1;
|
||||||
|
|
@ -369,6 +393,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */,
|
D05B9F7A29E39EED008CB1F9 /* Assets.xcassets in Resources */,
|
||||||
|
D09150422B9D2AF700BE3CB0 /* MainMenu.xib in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
@ -423,12 +448,19 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
D0FAB59A2B818B9600F6A84B /* Network.swift in Sources */,
|
||||||
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */,
|
D0BCC6082A0981FE00AD070D /* Tunnel.swift in Sources */,
|
||||||
43AA26D82A10004900F14CE6 /* MenuView.swift in Sources */,
|
D0FAB5982B818B8200F6A84B /* TunnelStatusView.swift in Sources */,
|
||||||
D05B9F7829E39EEC008CB1F9 /* TunnelView.swift in Sources */,
|
43AA26D82A10004900F14CE6 /* MenuItemToggleView.swift in Sources */,
|
||||||
D0BCC5FF2A086E1C00AD070D /* Status.swift in Sources */,
|
D05B9F7829E39EEC008CB1F9 /* BurrowView.swift in Sources */,
|
||||||
|
D0FAB5922B818A5900F6A84B /* NetworkExtensionTunnel.swift in Sources */,
|
||||||
|
D0FAB5962B818B2900F6A84B /* TunnelButton.swift in Sources */,
|
||||||
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */,
|
D00AA8972A4669BC005C8102 /* AppDelegate.swift in Sources */,
|
||||||
|
D05EF8C82B81818D0017AB4F /* FloatingButtonStyle.swift in Sources */,
|
||||||
|
D032E6522B8A79C20006B8AD /* HackClub.swift in Sources */,
|
||||||
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */,
|
D05B9F7629E39EEC008CB1F9 /* BurrowApp.swift in Sources */,
|
||||||
|
D01A79312B81630D0024EC91 /* NetworkView.swift in Sources */,
|
||||||
|
D032E6542B8A79DA0006B8AD /* WireGuard.swift in Sources */,
|
||||||
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */,
|
D0BCC5FD2A086D4700AD070D /* NetworkExtension+Async.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
@ -568,8 +600,8 @@
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/realm/SwiftLint.git";
|
repositoryURL = "https://github.com/realm/SwiftLint.git";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = upToNextMajorVersion;
|
branch = main;
|
||||||
minimumVersion = 0.54.0;
|
kind = branch;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-argument-parser.git",
|
"location" : "https://github.com/apple/swift-argument-parser.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a",
|
"revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531",
|
||||||
"version" : "1.2.2"
|
"version" : "1.2.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -41,8 +41,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-syntax.git",
|
"location" : "https://github.com/apple/swift-syntax.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036",
|
"revision" : "64889f0c732f210a935a0ad7cda38f77f876262d",
|
||||||
"version" : "509.0.2"
|
"version" : "509.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -50,8 +50,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/realm/SwiftLint.git",
|
"location" : "https://github.com/realm/SwiftLint.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee",
|
"branch" : "main",
|
||||||
"version" : "0.54.0"
|
"revision" : "7595ad3fafc1a31086dc40ba01fd898bf6b42d5f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -68,8 +68,8 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/drmohundro/SWXMLHash.git",
|
"location" : "https://github.com/drmohundro/SWXMLHash.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "4d0f62f561458cbe1f732171e625f03195151b60",
|
"revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f",
|
||||||
"version" : "7.0.1"
|
"version" : "7.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1510"
|
LastUpgradeVersion = "1520"
|
||||||
version = "1.7">
|
version = "1.7">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1510"
|
LastUpgradeVersion = "1520"
|
||||||
version = "2.0">
|
version = "2.0">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,16 @@ import os
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
private let logger = Logger.logger(for: PacketTunnelProvider.self)
|
private let logger = Logger.logger(for: PacketTunnelProvider.self)
|
||||||
|
|
||||||
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
override init() {
|
||||||
do {
|
do {
|
||||||
libburrow.spawnInProcess(socketPath: try Constants.socketURL.path)
|
libburrow.spawnInProcess(socketPath: try Constants.socketURL.path)
|
||||||
|
} catch {
|
||||||
|
logger.error("Failed to spawn: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func startTunnel(options: [String: NSObject]? = nil) async throws {
|
||||||
|
do {
|
||||||
let client = try Client()
|
let client = try Client()
|
||||||
|
|
||||||
let command = BurrowRequest(id: 0, command: "ServerConfig")
|
let command = BurrowRequest(id: 0, command: "ServerConfig")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ public enum Constants {
|
||||||
|
|
||||||
public static let bundleIdentifier = AppBundleIdentifier
|
public static let bundleIdentifier = AppBundleIdentifier
|
||||||
public static let appGroupIdentifier = AppGroupIdentifier
|
public static let appGroupIdentifier = AppGroupIdentifier
|
||||||
|
public static let networkExtensionBundleIdentifier = NetworkExtensionBundleIdentifier
|
||||||
|
|
||||||
public static var groupContainerURL: URL {
|
public static var groupContainerURL: URL {
|
||||||
get throws { try _groupContainerURL.get() }
|
get throws { try _groupContainerURL.get() }
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
static NSString * const AppBundleIdentifier = MACRO_STRING(APP_BUNDLE_IDENTIFIER);
|
static NSString * const AppBundleIdentifier = MACRO_STRING(APP_BUNDLE_IDENTIFIER);
|
||||||
static NSString * const AppGroupIdentifier = MACRO_STRING(APP_GROUP_IDENTIFIER);
|
static NSString * const AppGroupIdentifier = MACRO_STRING(APP_GROUP_IDENTIFIER);
|
||||||
|
static NSString * const NetworkExtensionBundleIdentifier = MACRO_STRING(NETWORK_EXTENSION_BUNDLE_IDENTIFIER);
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ PRODUCT_NAME = BurrowShared
|
||||||
MERGEABLE_LIBRARY = YES
|
MERGEABLE_LIBRARY = YES
|
||||||
|
|
||||||
SWIFT_INCLUDE_PATHS = $(PROJECT_DIR)/Shared/Constants
|
SWIFT_INCLUDE_PATHS = $(PROJECT_DIR)/Shared/Constants
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = APP_BUNDLE_IDENTIFIER=$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER=$(APP_GROUP_IDENTIFIER)
|
GCC_PREPROCESSOR_DEFINITIONS = APP_BUNDLE_IDENTIFIER=$(APP_BUNDLE_IDENTIFIER) APP_GROUP_IDENTIFIER=$(APP_GROUP_IDENTIFIER) NETWORK_EXTENSION_BUNDLE_IDENTIFIER=$(NETWORK_EXTENSION_BUNDLE_IDENTIFIER)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue