Compare commits
118 Commits
Author | SHA1 | Date | |
---|---|---|---|
18b103bf9c | |||
9b8de47f5d | |||
4f4c950349 | |||
331ca8a9a4 | |||
87e08e4607 | |||
845235a276 | |||
7587126a42 | |||
a85e016b4a | |||
fcdb4a8993 | |||
9134f08ec9 | |||
b281df3bbd | |||
a9f9c38e0d | |||
bbbdce6b44 | |||
237185995d | |||
48f950fc3a | |||
2ee0934080 | |||
cfbb83976d | |||
4e5b178e36 | |||
0c59d14da5 | |||
2a9c8e6830 | |||
78e750d764 | |||
c1c7561361 | |||
f90925ee17 | |||
ccfb15c83c | |||
bcc80b5210 | |||
023043a81d | |||
a2c93833df | |||
8b6267f411 | |||
1bff75311b | |||
7a580eef0c | |||
2306529936 | |||
08fdcdbc6c | |||
8cb8e5e409 | |||
6094f83e98 | |||
cdb4b60487 | |||
ae5bed2532 | |||
f098ffbf3d | |||
0a55b45c60 | |||
418d1e2479 | |||
fa3ec2aaa3 | |||
a1df2deec5 | |||
8fb43f7a16 | |||
8a8d32cb5d | |||
6e3bb1d322 | |||
cac7048d92 | |||
1aa051e12c | |||
58fd045bdf | |||
2fc71303df | |||
2c61d991d6 | |||
beca09b76f | |||
5c3e856a7b | |||
6fbbd499f8 | |||
1066bdc653 | |||
4ef5918b7a | |||
e45db50f48 | |||
8a7f045bb2 | |||
10272f3d7e | |||
96ce462021 | |||
8b635f2a14 | |||
f8f3fd5c9b | |||
6801283597 | |||
cf03727dc0 | |||
25a7dded73 | |||
c5fd43312f | |||
ea7572ffbb | |||
4d5b5de84b | |||
721bb32087 | |||
1fe086d53e | |||
347a9b7345 | |||
e0c087dbcb | |||
fd34deb7bd | |||
ccb0d96453 | |||
d821968a49 | |||
7d7a38546a | |||
30059353fe | |||
090440abc8 | |||
ceeba611d0 | |||
f1feb11baa | |||
90a02f364a | |||
6b8c68b058 | |||
fb34ea7c89 | |||
0c0045aead | |||
1fdf687c15 | |||
1040bcd037 | |||
655cd52ac4 | |||
2229f01eb0 | |||
f95eb7023f | |||
320e1ad3c5 | |||
e1880d9dfc | |||
998bbd231a | |||
fbf2158488 | |||
94df551b4b | |||
01288154a7 | |||
29b4fe2962 | |||
5907d9f689 | |||
86d74619b1 | |||
0c09e808bd | |||
da8659adba | |||
bbebe78997 | |||
19aa804cbb | |||
64491c6fe1 | |||
382cb177be | |||
e9f0d19d9a | |||
7709cd9f6c | |||
83eac7bf04 | |||
db8197df3d | |||
40e404fe24 | |||
e7f80ebce8 | |||
990152d77d | |||
2e61479c75 | |||
0579ebb1c1 | |||
f9df86851c | |||
b0c62e226a | |||
1392b6e4a0 | |||
12163bc87b | |||
f679231ade | |||
f725e09f55 | |||
99881bf4cd |
@@ -41,6 +41,12 @@ If you'd like to create a production build, choose "Any Mac" as the target and s
|
|||||||
10. Update Cask with new version + hash
|
10. Update Cask with new version + hash
|
||||||
11. Check new version can be installed via Cask
|
11. Check new version can be installed via Cask
|
||||||
|
|
||||||
|
## 🍱 Marketing Mode
|
||||||
|
|
||||||
|
You can enable marketing mode by setting the `PHPMON_MARKETING_MODE` environment variable. It preloads a list of (fake) domains in the domain window list for screenshot & marketing purposes.
|
||||||
|
|
||||||
|
launchctl setenv PHPMON_MARKETING_MODE true
|
||||||
|
|
||||||
## 🐛 Symbolication of crashes
|
## 🐛 Symbolication of crashes
|
||||||
|
|
||||||
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
|
5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5489625728312FAD004F647A /* CreatedFromFile.swift */; };
|
||||||
|
5489625928313231004F647A /* CreatedFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5489625728312FAD004F647A /* CreatedFromFile.swift */; };
|
||||||
54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */ = {isa = PBXBuildFile; fileRef = 54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */; };
|
54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */ = {isa = PBXBuildFile; fileRef = 54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */; };
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
@@ -43,9 +45,10 @@
|
|||||||
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||||
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||||
|
C40C5C9C2846A40600E28255 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
||||||
|
C40C5C9D2846A40600E28255 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; };
|
||||||
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||||
@@ -75,6 +78,7 @@
|
|||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
||||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||||
|
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */; };
|
||||||
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
||||||
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
@@ -82,14 +86,20 @@
|
|||||||
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
|
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
|
||||||
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
||||||
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
||||||
|
C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDA928A2C49900CEAC97 /* WarningListView.swift */; };
|
||||||
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
||||||
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
|
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
||||||
|
C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; };
|
||||||
|
C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; };
|
||||||
|
C4297F7A28970D59004C4630 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
||||||
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
||||||
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; };
|
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; };
|
||||||
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; };
|
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; };
|
||||||
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; };
|
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; };
|
||||||
|
C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; };
|
||||||
|
C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; };
|
||||||
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
||||||
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
||||||
C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; };
|
C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; };
|
||||||
@@ -102,22 +112,31 @@
|
|||||||
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
||||||
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
||||||
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
||||||
|
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
|
||||||
|
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
|
||||||
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
||||||
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
||||||
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
||||||
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
||||||
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
|
||||||
|
C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
||||||
|
C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
|
||||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||||
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||||
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
||||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
|
||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||||
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||||
|
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; };
|
||||||
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; };
|
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; };
|
||||||
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; };
|
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
|
||||||
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; };
|
C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
|
||||||
|
C463E380284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
||||||
|
C463E381284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
|
||||||
|
C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; };
|
||||||
|
C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; };
|
||||||
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
||||||
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
||||||
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; };
|
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; };
|
||||||
@@ -125,9 +144,15 @@
|
|||||||
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
|
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
|
||||||
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */; };
|
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */; };
|
||||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||||
|
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; };
|
||||||
|
C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; };
|
||||||
|
C46FA98C2822F08F00D78807 /* PhpConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA98A2822F08F00D78807 /* PhpConfigurationTest.swift */; };
|
||||||
|
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4709CA128524B3400088BB8 /* StatsView.swift */; };
|
||||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
|
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
||||||
|
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
|
||||||
|
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
|
||||||
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
|
||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
||||||
@@ -135,11 +160,24 @@
|
|||||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
||||||
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
||||||
C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C8F25CC7FD000CC7490 /* StatsView.xib */; };
|
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */; };
|
||||||
|
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
|
||||||
|
C485706F28BF452300539B36 /* WarningsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */; };
|
||||||
|
C485707028BF452300539B36 /* WarningsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */; };
|
||||||
|
C485707128BF452E00539B36 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
|
||||||
|
C485707228BF453800539B36 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
|
||||||
|
C485707328BF454300539B36 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */; };
|
||||||
|
C485707428BF454E00539B36 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* ServicesView.swift */; };
|
||||||
|
C485707528BF454F00539B36 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4709CA128524B3400088BB8 /* StatsView.swift */; };
|
||||||
|
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* SectionHeaderView.swift */; };
|
||||||
|
C485707728BF455300539B36 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; };
|
||||||
|
C485707828BF456300539B36 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
|
||||||
|
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; };
|
||||||
|
C485707A28BF457800539B36 /* WarningListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDA928A2C49900CEAC97 /* WarningListView.swift */; };
|
||||||
|
C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
|
||||||
|
C485707C28BF459500539B36 /* NoWarningsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */; };
|
||||||
|
C485707D28BF45A200539B36 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
|
||||||
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
||||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
|
|
||||||
C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C9925CC888B00CC7490 /* HeaderView.xib */; };
|
|
||||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
|
||||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
||||||
@@ -147,12 +185,12 @@
|
|||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; };
|
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
||||||
|
C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
|
||||||
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
||||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
||||||
C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
|
||||||
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */; };
|
C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */; };
|
||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
||||||
@@ -167,6 +205,8 @@
|
|||||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||||
|
C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* SectionHeaderView.swift */; };
|
||||||
|
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* ServicesView.swift */; };
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
||||||
@@ -192,12 +232,14 @@
|
|||||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
|
C4CDA893288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
||||||
|
C4CDA894288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
|
||||||
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
||||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
||||||
C4D5CFCA27E0F9CD00035329 /* NginxConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */; };
|
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
|
||||||
C4D5CFCB27E0F9CD00035329 /* NginxConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */; };
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */; };
|
||||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
||||||
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||||
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
||||||
@@ -214,15 +256,11 @@
|
|||||||
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
||||||
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||||
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */; };
|
||||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; };
|
||||||
|
C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; };
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
|
||||||
C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
|
||||||
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A727708B9E001DF387 /* Preview.swift */; };
|
|
||||||
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A827708B9E001DF387 /* PMStatsView.swift */; };
|
|
||||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A827708B9E001DF387 /* PMStatsView.swift */; };
|
|
||||||
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
|
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
|
||||||
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
|
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EED88827A48778006D7272 /* InterAppHandler.swift */; };
|
||||||
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; };
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; };
|
||||||
@@ -231,12 +269,12 @@
|
|||||||
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
|
||||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||||
|
C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; };
|
||||||
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
|
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; };
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; };
|
||||||
@@ -246,19 +284,21 @@
|
|||||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||||
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
||||||
C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
|
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
||||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
||||||
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
||||||
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
|
||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||||
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
||||||
|
C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; };
|
||||||
|
C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; };
|
||||||
|
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
|
||||||
|
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; };
|
||||||
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
||||||
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -276,6 +316,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
||||||
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
|
5489625728312FAD004F647A /* CreatedFromFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreatedFromFile.swift; sourceTree = "<group>"; };
|
||||||
54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy-custom-tld.test"; sourceTree = "<group>"; };
|
54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy-custom-tld.test"; sourceTree = "<group>"; };
|
||||||
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeysController.swift; sourceTree = "<group>"; };
|
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeysController.swift; sourceTree = "<group>"; };
|
||||||
@@ -296,6 +337,7 @@
|
|||||||
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
||||||
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlert.swift; sourceTree = "<group>"; };
|
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlert.swift; sourceTree = "<group>"; };
|
||||||
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlertVC.swift; sourceTree = "<group>"; };
|
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlertVC.swift; sourceTree = "<group>"; };
|
||||||
|
C40C5C9B2846A40600E28255 /* Preset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preset.swift; sourceTree = "<group>"; };
|
||||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
||||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
||||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||||
@@ -318,17 +360,23 @@
|
|||||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
|
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
||||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
|
C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoWarningsView.swift; sourceTree = "<group>"; };
|
||||||
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = "<group>"; };
|
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = "<group>"; };
|
||||||
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
||||||
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
||||||
C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = "<group>"; };
|
C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = "<group>"; };
|
||||||
|
C422DDA928A2C49900CEAC97 /* WarningListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningListView.swift; sourceTree = "<group>"; };
|
||||||
|
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningsWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
||||||
C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = "<group>"; };
|
C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = "<group>"; };
|
||||||
C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; };
|
C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; };
|
||||||
|
C42800A928452AA10099C999 /* StatusMenu+Items.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Items.swift"; sourceTree = "<group>"; };
|
||||||
|
C4297F7928970D59004C4630 /* WarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningView.swift; sourceTree = "<group>"; };
|
||||||
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; };
|
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; };
|
||||||
C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = "<group>"; };
|
C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = "<group>"; };
|
||||||
C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = "<group>"; };
|
C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = "<group>"; };
|
||||||
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = "<group>"; };
|
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = "<group>"; };
|
||||||
|
C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shell+PATH.swift"; sourceTree = "<group>"; };
|
||||||
C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = "<group>"; };
|
C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = "<group>"; };
|
||||||
C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = "<group>"; };
|
C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = "<group>"; };
|
||||||
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; };
|
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; };
|
||||||
@@ -339,35 +387,42 @@
|
|||||||
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
|
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
|
||||||
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
|
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
|
||||||
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; };
|
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; };
|
||||||
|
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; };
|
||||||
|
C44A874728905BB000498BC4 /* ProgressVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressVC.swift; sourceTree = "<group>"; };
|
||||||
|
C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalProgressWindowController.swift; sourceTree = "<group>"; };
|
||||||
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
|
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
|
||||||
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
|
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
|
||||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
|
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
|
||||||
|
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
|
||||||
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; };
|
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; };
|
||||||
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = "<group>"; };
|
C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; };
|
||||||
|
C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; };
|
||||||
|
C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWindowController.swift; sourceTree = "<group>"; };
|
||||||
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; };
|
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; };
|
||||||
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
|
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
|
||||||
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
|
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
|
||||||
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdaterCheckTest.swift; sourceTree = "<group>"; };
|
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdaterCheckTest.swift; sourceTree = "<group>"; };
|
||||||
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
||||||
|
C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigurationFile.swift; sourceTree = "<group>"; };
|
||||||
|
C46FA98A2822F08F00D78807 /* PhpConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigurationTest.swift; sourceTree = "<group>"; };
|
||||||
|
C4709CA128524B3400088BB8 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||||
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
||||||
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
|
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
|
||||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
|
C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
|
||||||
|
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; };
|
||||||
|
C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; };
|
||||||
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
||||||
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
|
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
|
||||||
C484437A2804BB560041A78A /* ValetProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxyScanner.swift; sourceTree = "<group>"; };
|
C484437A2804BB560041A78A /* ValetProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxyScanner.swift; sourceTree = "<group>"; };
|
||||||
C48D0C8F25CC7FD000CC7490 /* StatsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsView.xib; sourceTree = "<group>"; };
|
|
||||||
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
|
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
|
||||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
|
||||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = "<group>"; };
|
|
||||||
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
|
||||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
||||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
||||||
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
||||||
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = "<group>"; };
|
||||||
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
||||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
|
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
|
||||||
@@ -379,6 +434,8 @@
|
|||||||
C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
|
C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
|
||||||
C4B5853C2770FE3900DA4FBE /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
|
C4B5853C2770FE3900DA4FBE /* Shell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
|
||||||
C4B5853D2770FE3900DA4FBE /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
C4B5853D2770FE3900DA4FBE /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
||||||
|
C4B609192853AAD300C95265 /* SectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderView.swift; sourceTree = "<group>"; };
|
||||||
|
C4B6091C2853AB9700C95265 /* ServicesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
|
||||||
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
||||||
@@ -392,9 +449,10 @@
|
|||||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||||
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
C4CDA892288F1A71007CE25F /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
|
||||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
||||||
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
|
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
|
||||||
C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfiguration.swift; sourceTree = "<group>"; };
|
C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = "<group>"; };
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
||||||
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
|
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
|
||||||
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpHelper.swift; sourceTree = "<group>"; };
|
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpHelper.swift; sourceTree = "<group>"; };
|
||||||
@@ -406,18 +464,17 @@
|
|||||||
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
||||||
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
||||||
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
|
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
|
||||||
C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = "<group>"; };
|
C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
||||||
C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
|
C4EB53E428551F9B006F9937 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
||||||
|
C4EB53E628553117006F9937 /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.swift; sourceTree = "<group>"; };
|
||||||
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
|
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
|
||||||
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = "<group>"; };
|
|
||||||
C4EE55A727708B9E001DF387 /* Preview.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = "<group>"; };
|
|
||||||
C4EE55A827708B9E001DF387 /* PMStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMStatsView.swift; sourceTree = "<group>"; };
|
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterAppHandler.swift; sourceTree = "<group>"; };
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterAppHandler.swift; sourceTree = "<group>"; };
|
||||||
C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = "<group>"; };
|
C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = "<group>"; };
|
||||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = "<group>"; };
|
C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = "<group>"; };
|
||||||
C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = "<group>"; };
|
C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = "<group>"; };
|
||||||
|
C4F361602836BFD9003598CC /* MainMenu+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Actions.swift"; sourceTree = "<group>"; };
|
||||||
C4F5FBCC28218C93001065C5 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
|
C4F5FBCC28218C93001065C5 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
|
||||||
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
@@ -426,6 +483,8 @@
|
|||||||
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = "<group>"; };
|
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = "<group>"; };
|
||||||
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
|
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
|
||||||
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
|
C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreferencesWindowController+Hotkey.swift"; sourceTree = "<group>"; };
|
||||||
|
C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; };
|
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; };
|
||||||
C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; };
|
C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -451,7 +510,8 @@
|
|||||||
5420395726135DB800FB00FA /* Preferences */ = {
|
5420395726135DB800FB00FA /* Preferences */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4998F092617633900B2526E /* PrefsWC.swift */,
|
C4998F092617633900B2526E /* PreferencesWindowController.swift */,
|
||||||
|
C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */,
|
||||||
5420395826135DC100FB00FA /* PrefsVC.swift */,
|
5420395826135DC100FB00FA /* PrefsVC.swift */,
|
||||||
5420395E2613607600FB00FA /* Preferences.swift */,
|
5420395E2613607600FB00FA /* Preferences.swift */,
|
||||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
|
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
|
||||||
@@ -459,10 +519,19 @@
|
|||||||
C4DEB7D327A5D60B00834718 /* Stats.swift */,
|
C4DEB7D327A5D60B00834718 /* Stats.swift */,
|
||||||
C41CD0272628D8E20065BBED /* Keybinds */,
|
C41CD0272628D8E20065BBED /* Keybinds */,
|
||||||
54FCFD28276C88C0004CE748 /* Views */,
|
54FCFD28276C88C0004CE748 /* Views */,
|
||||||
|
C4CDA892288F1A71007CE25F /* Keys.swift */,
|
||||||
);
|
);
|
||||||
path = Preferences;
|
path = Preferences;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5489625628312F95004F647A /* Protocols */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5489625728312FAD004F647A /* CreatedFromFile.swift */,
|
||||||
|
);
|
||||||
|
path = Protocols;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
54B20EDF263AA22C00D3250E /* PHP */ = {
|
54B20EDF263AA22C00D3250E /* PHP */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -473,6 +542,7 @@
|
|||||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
||||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
||||||
|
C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */,
|
||||||
);
|
);
|
||||||
path = PHP;
|
path = PHP;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -529,9 +599,19 @@
|
|||||||
path = Notice;
|
path = Notice;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C40C5C9E2846A42D00E28255 /* Presets */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C40C5C9B2846A40600E28255 /* Preset.swift */,
|
||||||
|
C463E37F284930EE00422731 /* PresetHelper.swift */,
|
||||||
|
);
|
||||||
|
path = Presets;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C44F868C2835BD60005C353A /* phpmon */,
|
||||||
C459B4C127F6097E00E9B4B4 /* php */,
|
C459B4C127F6097E00E9B4B4 /* php */,
|
||||||
C459B4C027F6096300E9B4B4 /* valet */,
|
C459B4C027F6096300E9B4B4 /* valet */,
|
||||||
C459B4BF27F6094100E9B4B4 /* brew */,
|
C459B4BF27F6094100E9B4B4 /* brew */,
|
||||||
@@ -549,6 +629,7 @@
|
|||||||
C4B5853D2770FE3900DA4FBE /* Command.swift */,
|
C4B5853D2770FE3900DA4FBE /* Command.swift */,
|
||||||
C4B5853B2770FE3900DA4FBE /* Paths.swift */,
|
C4B5853B2770FE3900DA4FBE /* Paths.swift */,
|
||||||
C4B5853C2770FE3900DA4FBE /* Shell.swift */,
|
C4B5853C2770FE3900DA4FBE /* Shell.swift */,
|
||||||
|
C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */,
|
||||||
C4C1019A27C65C6F001FACC2 /* Process.swift */,
|
C4C1019A27C65C6F001FACC2 /* Process.swift */,
|
||||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
|
C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
|
||||||
C417DC73277614690015E6EE /* Helpers.swift */,
|
C417DC73277614690015E6EE /* Helpers.swift */,
|
||||||
@@ -614,14 +695,27 @@
|
|||||||
C4D9ADBD27761084007277F4 /* PHP */,
|
C4D9ADBD27761084007277F4 /* PHP */,
|
||||||
C47331A0247093AC009A0597 /* Menu */,
|
C47331A0247093AC009A0597 /* Menu */,
|
||||||
C464ADAA275A7A25003FCD53 /* DomainList */,
|
C464ADAA275A7A25003FCD53 /* DomainList */,
|
||||||
|
C422DDAB28A2DAA100CEAC97 /* Warnings */,
|
||||||
|
C44A874628905B8500498BC4 /* Onboarding */,
|
||||||
5420395726135DB800FB00FA /* Preferences */,
|
5420395726135DB800FB00FA /* Preferences */,
|
||||||
C44C198F276E3A380072762D /* Progress */,
|
C44C198F276E3A380072762D /* Progress */,
|
||||||
C4C8E81D276F5686003AC782 /* Watcher */,
|
C4C8E81D276F5686003AC782 /* Watcher */,
|
||||||
|
C40C5C9E2846A42D00E28255 /* Presets */,
|
||||||
C4EE55B027708BB2001DF387 /* SwiftUI */,
|
C4EE55B027708BB2001DF387 /* SwiftUI */,
|
||||||
);
|
);
|
||||||
path = Domain;
|
path = Domain;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C47699F028A2F3150060FEB8 /* Warning.swift */,
|
||||||
|
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
|
||||||
|
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
|
||||||
|
);
|
||||||
|
path = Warnings;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C42337A1281F19DC00459A48 /* Extensions */ = {
|
C42337A1281F19DC00459A48 /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -630,6 +724,16 @@
|
|||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4297F7828970D4E004C4630 /* Warning */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4297F7928970D59004C4630 /* WarningView.swift */,
|
||||||
|
C422DDA928A2C49900CEAC97 /* WarningListView.swift */,
|
||||||
|
C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */,
|
||||||
|
);
|
||||||
|
path = Warning;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44067F327E256560045BD4E /* Cells */ = {
|
C44067F327E256560045BD4E /* Cells */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -643,10 +747,19 @@
|
|||||||
path = Cells;
|
path = Cells;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C44A874628905B8500498BC4 /* Onboarding */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */,
|
||||||
|
);
|
||||||
|
path = Onboarding;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44C198F276E3A380072762D /* Progress */ = {
|
C44C198F276E3A380072762D /* Progress */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C44C198C276E3A1C0072762D /* ProgressWindow.swift */,
|
C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */,
|
||||||
|
C44A874728905BB000498BC4 /* ProgressVC.swift */,
|
||||||
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */,
|
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */,
|
||||||
);
|
);
|
||||||
path = Progress;
|
path = Progress;
|
||||||
@@ -661,6 +774,14 @@
|
|||||||
path = Errors;
|
path = Errors;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C44F868C2835BD60005C353A /* phpmon */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C44F868D2835BD8D005C353A /* phpmon-config.json */,
|
||||||
|
);
|
||||||
|
path = phpmon;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C459B4BE27F6093A00E9B4B4 /* nginx */ = {
|
C459B4BE27F6093A00E9B4B4 /* nginx */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -702,7 +823,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C44067F327E256560045BD4E /* Cells */,
|
C44067F327E256560045BD4E /* Cells */,
|
||||||
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */,
|
C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */,
|
||||||
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */,
|
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */,
|
||||||
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */,
|
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */,
|
||||||
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */,
|
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */,
|
||||||
@@ -721,13 +842,9 @@
|
|||||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
||||||
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */,
|
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */,
|
||||||
|
C4F361602836BFD9003598CC /* MainMenu+Actions.swift */,
|
||||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
C42800A928452AA10099C999 /* StatusMenu+Items.swift */,
|
||||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
|
||||||
C48D0CA225CC992000CC7490 /* StatsView.swift */,
|
|
||||||
C48D0C8F25CC7FD000CC7490 /* StatsView.xib */,
|
|
||||||
C4EC1E67279DE0540010F296 /* ServicesView.swift */,
|
|
||||||
C4EC1E65279DE0380010F296 /* ServicesView.xib */,
|
|
||||||
);
|
);
|
||||||
path = Menu;
|
path = Menu;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -799,8 +916,10 @@
|
|||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
|
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
|
||||||
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
|
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
|
||||||
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
||||||
|
C45E76132854A65300B4FE0C /* ServicesManager.swift */,
|
||||||
);
|
);
|
||||||
path = App;
|
path = App;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -813,10 +932,38 @@
|
|||||||
C44CCD4327AFE93300CE40E5 /* Errors */,
|
C44CCD4327AFE93300CE40E5 /* Errors */,
|
||||||
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
||||||
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
||||||
|
5489625628312F95004F647A /* Protocols */,
|
||||||
);
|
);
|
||||||
path = Common;
|
path = Common;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4B609162853AA9A00C95265 /* Common */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */,
|
||||||
|
);
|
||||||
|
path = Common;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4B609172853AA9E00C95265 /* Menu */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4B6091C2853AB9700C95265 /* ServicesView.swift */,
|
||||||
|
C4709CA128524B3400088BB8 /* StatsView.swift */,
|
||||||
|
C4B609192853AAD300C95265 /* SectionHeaderView.swift */,
|
||||||
|
C4EB53E428551F9B006F9937 /* HeaderView.swift */,
|
||||||
|
);
|
||||||
|
path = Menu;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4B609182853AAA700C95265 /* Domains */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */,
|
||||||
|
);
|
||||||
|
path = Domains;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C4C0E8D827F887A5002D32A9 /* Sites */ = {
|
C4C0E8D827F887A5002D32A9 /* Sites */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -840,7 +987,7 @@
|
|||||||
C4C0E8DA27F887CC002D32A9 /* Nginx */ = {
|
C4C0E8DA27F887CC002D32A9 /* Nginx */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */,
|
C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */,
|
||||||
);
|
);
|
||||||
path = Nginx;
|
path = Nginx;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -869,6 +1016,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */,
|
C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */,
|
||||||
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */,
|
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */,
|
||||||
|
C46FA98A2822F08F00D78807 /* PhpConfigurationTest.swift */,
|
||||||
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
|
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
|
||||||
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
|
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
|
||||||
);
|
);
|
||||||
@@ -939,13 +1087,22 @@
|
|||||||
path = Switcher;
|
path = Switcher;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4E9D2BE2878B32D008FFDAD /* Onboarding */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */,
|
||||||
|
);
|
||||||
|
path = Onboarding;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C4EE55B027708BB2001DF387 /* SwiftUI */ = {
|
C4EE55B027708BB2001DF387 /* SwiftUI */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C49E171E27A5736E00787921 /* PMServicesView.swift */,
|
C4297F7828970D4E004C4630 /* Warning */,
|
||||||
C4EE55A627708B9E001DF387 /* PMHeaderView.swift */,
|
C4E9D2BE2878B32D008FFDAD /* Onboarding */,
|
||||||
C4EE55A827708B9E001DF387 /* PMStatsView.swift */,
|
C4B609182853AAA700C95265 /* Domains */,
|
||||||
C4EE55A727708B9E001DF387 /* Preview.swift */,
|
C4B609172853AA9E00C95265 /* Menu */,
|
||||||
|
C4B609162853AA9A00C95265 /* Common */,
|
||||||
);
|
);
|
||||||
path = SwiftUI;
|
path = SwiftUI;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -980,6 +1137,7 @@
|
|||||||
C48D0C9225CC804200CC7490 /* XibLoadable.swift */,
|
C48D0C9225CC804200CC7490 /* XibLoadable.swift */,
|
||||||
C42759662627662800093CAE /* NSMenuExtension.swift */,
|
C42759662627662800093CAE /* NSMenuExtension.swift */,
|
||||||
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */,
|
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */,
|
||||||
|
C4EB53E628553117006F9937 /* ArrayExtension.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1073,19 +1231,14 @@
|
|||||||
files = (
|
files = (
|
||||||
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */,
|
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */,
|
||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */,
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */,
|
||||||
C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */,
|
|
||||||
C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */,
|
|
||||||
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
|
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
|
||||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||||
C4232EE52612526500158FC6 /* Credits.html in Resources */,
|
C4232EE52612526500158FC6 /* Credits.html in Resources */,
|
||||||
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
|
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
|
||||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */,
|
|
||||||
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */,
|
|
||||||
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||||
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
|
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
|
||||||
C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1098,6 +1251,7 @@
|
|||||||
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */,
|
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */,
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
||||||
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
|
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */,
|
||||||
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */,
|
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */,
|
||||||
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */,
|
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */,
|
||||||
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||||
@@ -1137,46 +1291,56 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */,
|
||||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
|
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
|
||||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
||||||
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */,
|
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */,
|
||||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
|
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */,
|
||||||
|
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
|
||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
|
C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
|
||||||
|
C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */,
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
||||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
|
5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */,
|
||||||
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||||
|
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */,
|
||||||
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
||||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
|
||||||
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
|
||||||
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
|
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
|
||||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */,
|
|
||||||
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
||||||
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
||||||
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */,
|
|
||||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
||||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
C463E380284930EE00422731 /* PresetHelper.swift in Sources */,
|
||||||
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
|
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
|
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
|
||||||
|
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */,
|
||||||
|
C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */,
|
||||||
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
|
C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */,
|
||||||
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
||||||
|
C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */,
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
||||||
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */,
|
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */,
|
||||||
|
C40C5C9C2846A40600E28255 /* Preset.swift in Sources */,
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */,
|
C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */,
|
||||||
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */,
|
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */,
|
||||||
|
C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */,
|
||||||
|
C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */,
|
||||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
||||||
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */,
|
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */,
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C4CDA893288F1A71007CE25F /* Keys.swift in Sources */,
|
||||||
|
C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */,
|
||||||
|
C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */,
|
||||||
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
||||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
||||||
@@ -1185,14 +1349,17 @@
|
|||||||
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */,
|
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||||
|
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
|
C4297F7A28970D59004C4630 /* WarningView.swift in Sources */,
|
||||||
C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
||||||
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */,
|
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */,
|
||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */,
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */,
|
||||||
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
||||||
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||||
|
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */,
|
||||||
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
||||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
@@ -1200,13 +1367,16 @@
|
|||||||
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
|
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */,
|
||||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
|
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
|
||||||
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
|
C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */,
|
||||||
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
|
||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
@@ -1216,17 +1386,20 @@
|
|||||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
||||||
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
||||||
|
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */,
|
||||||
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
||||||
|
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */,
|
||||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||||
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
|
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||||
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
||||||
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
||||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
|
||||||
C4D5CFCA27E0F9CD00035329 /* NginxConfiguration.swift in Sources */,
|
C485707028BF452300539B36 /* WarningsWindowController.swift in Sources */,
|
||||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
|
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
|
||||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
@@ -1237,10 +1410,11 @@
|
|||||||
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */,
|
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */,
|
||||||
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */,
|
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */,
|
||||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
|
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
|
||||||
|
C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */,
|
||||||
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */,
|
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */,
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
||||||
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */,
|
C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */,
|
||||||
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */,
|
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */,
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
@@ -1254,8 +1428,9 @@
|
|||||||
files = (
|
files = (
|
||||||
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */,
|
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */,
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C485707128BF452E00539B36 /* WarningManager.swift in Sources */,
|
||||||
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||||
|
C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */,
|
||||||
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||||
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */,
|
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */,
|
||||||
@@ -1266,20 +1441,27 @@
|
|||||||
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
|
C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */,
|
||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */,
|
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */,
|
||||||
|
C485707528BF454F00539B36 /* StatsView.swift in Sources */,
|
||||||
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||||
|
C485707328BF454300539B36 /* OnboardingView.swift in Sources */,
|
||||||
|
C485707728BF455300539B36 /* HeaderView.swift in Sources */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
C4D5CFCB27E0F9CD00035329 /* NginxConfiguration.swift in Sources */,
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
|
||||||
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
||||||
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
||||||
|
C485707A28BF457800539B36 /* WarningListView.swift in Sources */,
|
||||||
C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
||||||
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */,
|
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */,
|
||||||
|
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */,
|
||||||
|
C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||||
@@ -1291,24 +1473,34 @@
|
|||||||
C4C0E8E327F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
C4C0E8E327F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
|
C463E381284930EE00422731 /* PresetHelper.swift in Sources */,
|
||||||
|
C46FA98C2822F08F00D78807 /* PhpConfigurationTest.swift in Sources */,
|
||||||
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
|
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
|
||||||
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
|
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
|
||||||
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
|
C485707428BF454E00539B36 /* ServicesView.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */,
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
|
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */,
|
||||||
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
||||||
|
C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */,
|
||||||
|
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */,
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
||||||
|
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */,
|
||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||||
|
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||||
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
|
C40C5C9D2846A40600E28255 /* Preset.swift in Sources */,
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
||||||
|
C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */,
|
||||||
C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
||||||
@@ -1318,26 +1510,31 @@
|
|||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||||
|
5489625928313231004F647A /* CreatedFromFile.swift in Sources */,
|
||||||
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
||||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||||
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */,
|
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */,
|
||||||
|
C485707D28BF45A200539B36 /* WarningView.swift in Sources */,
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
||||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
|
C4CDA894288F1A71007CE25F /* Keys.swift in Sources */,
|
||||||
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */,
|
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */,
|
||||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
||||||
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */,
|
||||||
|
C485707228BF453800539B36 /* SwiftUIHelper.swift in Sources */,
|
||||||
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */,
|
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */,
|
||||||
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
|
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
|
||||||
C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */,
|
C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */,
|
||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C485707828BF456300539B36 /* Warning.swift in Sources */,
|
||||||
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */,
|
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */,
|
||||||
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */,
|
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */,
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
||||||
|
C485707C28BF459500539B36 /* NoWarningsView.swift in Sources */,
|
||||||
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
|
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||||
@@ -1345,14 +1542,16 @@
|
|||||||
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
|
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
|
||||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
|
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
|
||||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
|
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
|
||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
|
||||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||||
|
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */,
|
||||||
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
|
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
|
||||||
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */,
|
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */,
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
|
C485706F28BF452300539B36 /* WarningsWindowController.swift in Sources */,
|
||||||
|
C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
|
||||||
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||||
C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
||||||
@@ -1360,15 +1559,14 @@
|
|||||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
||||||
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||||
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */,
|
C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */,
|
||||||
|
C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */,
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
||||||
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */,
|
|
||||||
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */,
|
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */,
|
||||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
|
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
|
||||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
||||||
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */,
|
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */,
|
||||||
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
|
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
|
||||||
C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
|
||||||
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1516,11 +1714,12 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 787;
|
CURRENT_PROJECT_VERSION = 955;
|
||||||
DEBUG = YES;
|
DEBUG = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1530,7 +1729,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.3.1;
|
MARKETING_VERSION = 5.5.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -1542,11 +1741,12 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppColor;
|
||||||
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
|
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 787;
|
CURRENT_PROJECT_VERSION = 955;
|
||||||
DEBUG = NO;
|
DEBUG = NO;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1556,7 +1756,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.3.1;
|
MARKETING_VERSION = 5.5.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@@ -69,8 +69,13 @@
|
|||||||
</CommandLineArguments>
|
</CommandLineArguments>
|
||||||
<EnvironmentVariables>
|
<EnvironmentVariables>
|
||||||
<EnvironmentVariable
|
<EnvironmentVariable
|
||||||
key = "PHPMON_MARKETING_MODE"
|
key = "EXTREME_DOCTOR_MODE"
|
||||||
value = "YES"
|
value = ""
|
||||||
|
isEnabled = "NO">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "PAINT_PHPMON_SWIFTUI_VIEWS"
|
||||||
|
value = ""
|
||||||
isEnabled = "NO">
|
isEnabled = "NO">
|
||||||
</EnvironmentVariable>
|
</EnvironmentVariable>
|
||||||
</EnvironmentVariables>
|
</EnvironmentVariables>
|
||||||
|
167
README.md
@@ -1,16 +1,19 @@
|
|||||||
|
> **Note**
|
||||||
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
|
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
|
||||||
|
|
||||||
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
||||||
|
|
||||||
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this app</u> (consult the FAQ below with info about how to set up your environment).
|
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this app</u> (consult the FAQ below with info about how to set up your environment).
|
||||||
|
|
||||||
<img src="./docs/screenshot.jpg" width="1085px" alt="phpmon screenshot (menu bar app)"/>
|
<img src="./docs/screenshot.jpg#gh-light-mode-only" width="1280px" alt="phpmon screenshot (menu bar app)"/>
|
||||||
|
<img src="./docs/screenshot-dark.jpg#gh-dark-mode-only" width="1280px" alt="phpmon screenshot (menu bar app)"/>
|
||||||
|
|
||||||
<small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small>
|
<small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small>
|
||||||
|
|
||||||
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
|
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
|
||||||
|
|
||||||
<img src="./docs/notification.png" width="370px" alt="phpmon screenshot (notification)"/>
|
<img src="./docs/notification.png#gh-light-mode-only" width="370px" alt="phpmon screenshot (notification)"/>
|
||||||
|
<img src="./docs/notification-dark.png#gh-dark-mode-only" width="370px" alt="phpmon screenshot (notification)"/>
|
||||||
|
|
||||||
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
|
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
|
||||||
|
|
||||||
@@ -21,10 +24,10 @@ You can also add new domains as links, isolate sites, manage various services, a
|
|||||||
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
|
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
|
||||||
|
|
||||||
* Your user account can administer your computer (required for some functionality, e.g. certificate generation)
|
* Your user account can administer your computer (required for some functionality, e.g. certificate generation)
|
||||||
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
* macOS 11 Big Sur or later
|
||||||
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
||||||
* Homebrew `php` formula is installed
|
* Homebrew `php` formula is installed
|
||||||
* Laravel Valet 2.16 or newer (supports Valet 3)
|
* Laravel Valet 3 recommended (but compatible with Valet 2)
|
||||||
|
|
||||||
_You may need to update your Valet installation to keep everything working if a major version update of PHP has been released. You can do this by running `composer global update && valet install`. Some features are not supported when running Valet 2._
|
_You may need to update your Valet installation to keep everything working if a major version update of PHP has been released. You can do this by running `composer global update && valet install`. Some features are not supported when running Valet 2._
|
||||||
|
|
||||||
@@ -103,14 +106,14 @@ Super convenient!
|
|||||||
<details>
|
<details>
|
||||||
<summary><strong>I want to set up PHP Monitor from scratch! I don't have Homebrew installed either, where do I begin?</strong></summary>
|
<summary><strong>I want to set up PHP Monitor from scratch! I don't have Homebrew installed either, where do I begin?</strong></summary>
|
||||||
|
|
||||||
If you want to set up your computer for the very first time with PHP Monitor, here's how I do it:
|
If you want to set up your computer for the very first time with PHP Monitor, here's how I do it.
|
||||||
|
|
||||||
Install [Homebrew](https://brew.sh) first.
|
**I have also created [a video tutorial](https://www.youtube.com/watch?v=fO3hVhkvm3w) which may be easier to follow. If you just want the terminal commands, keep reading.**
|
||||||
|
|
||||||
Install PHP, composer, add to path:
|
Install [Homebrew](https://brew.sh) first. Follow the instructions there first!
|
||||||
|
|
||||||
|
Then, you'll need to set up your PATH.
|
||||||
|
|
||||||
brew install php
|
|
||||||
brew install composer
|
|
||||||
nano .zshrc
|
nano .zshrc
|
||||||
|
|
||||||
Make sure the following line is not in the comments:
|
Make sure the following line is not in the comments:
|
||||||
@@ -123,30 +126,61 @@ If you're on an Apple Silicon-based Mac, you'll need to add:
|
|||||||
# on an M1 Mac
|
# on an M1 Mac
|
||||||
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
||||||
|
|
||||||
and add the following to your .zshrc, but add this BEFORE the homebrew PATH additions:
|
and add the following to your `.zshrc` file, but add this BEFORE the homebrew PATH additions:
|
||||||
|
|
||||||
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
||||||
|
|
||||||
If you're adding composer and Homebrew binaries, ensure that Homebrew binaries are preferred by adding these to the path last. On my system, that looks like this:
|
If you're adding `composer` and Homebrew binaries, ensure that Homebrew binaries are preferred by adding these to the path last. On my system, that looks like this:
|
||||||
|
|
||||||
export PATH=$HOME/bin:/usr/local/bin:$PATH
|
export PATH=$HOME/bin:/usr/local/bin:$PATH
|
||||||
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
||||||
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
||||||
|
|
||||||
|
If you are *not* on Apple Silicon, you should remove the third line.
|
||||||
|
|
||||||
|
Install the `php` and `composer` formulae:
|
||||||
|
|
||||||
|
brew install php composer
|
||||||
|
|
||||||
Make sure PHP is linked correctly:
|
Make sure PHP is linked correctly:
|
||||||
|
|
||||||
which php
|
which php
|
||||||
|
|
||||||
should return: `/usr/local/bin/php` (or `/opt/homebrew/bin/php`)
|
should return: `/usr/local/bin/php` (or `/opt/homebrew/bin/php` if you are on Apple Silicon)
|
||||||
|
|
||||||
composer global require laravel/valet
|
composer global require laravel/valet
|
||||||
|
|
||||||
|
For optimal results, you should lock your PHP platform for global dependencies to the oldest version of PHP you intend to run. If that version is PHP 7.0, your `~/.composer/composer.json` file could look like this (please adjust the version accordingly!):
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"laravel/valet": "^3.0",
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `composer global update` again. This ensures that when you switch to a different global PHP version, [Valet won't break](https://github.com/nicoverbruggen/phpmon/issues/178). If it does, PHP Monitor will let you know what you can do about this.
|
||||||
|
|
||||||
|
Then, install Valet:
|
||||||
|
|
||||||
valet install
|
valet install
|
||||||
|
|
||||||
This should install `dnsmasq` and set up Valet. Great, almost there!
|
This should install `dnsmasq` and set up Valet. Great, almost there!
|
||||||
|
|
||||||
valet trust
|
valet trust
|
||||||
|
|
||||||
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
You can now install PHP Monitor, if you haven't already:
|
||||||
|
|
||||||
|
brew tap nicoverbruggen/homebrew-cask
|
||||||
|
brew install --cask phpmon
|
||||||
|
|
||||||
|
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work. You will need to approve the initial launch of the app, but you should be ready to go now.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -297,6 +331,95 @@ The app includes an Internet Access Policy file, so if you're using something li
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>How do I various presets to show up?</strong></summary>
|
||||||
|
|
||||||
|
You must set these presets up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||||
|
|
||||||
|
You must have set up at least one valid preset for this presets to work in PHP Monitor.
|
||||||
|
|
||||||
|
Here's an example of a working preset:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
"scan_apps": [],
|
||||||
|
"services": [],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "Legacy Project",
|
||||||
|
"php": "8.0",
|
||||||
|
"extensions": {
|
||||||
|
"xdebug": false
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"memory_limit": "128M",
|
||||||
|
"upload_max_filesize": "128M",
|
||||||
|
"post_max_size": "128M"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"export": {}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version.
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> You must restart PHP Monitor for these changes to be detected.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>How do I ensure additional Homebrew services are shown in the app?</strong></summary>
|
||||||
|
|
||||||
|
You must set these services up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||||
|
|
||||||
|
You can specify custom services in the configuration file for Homebrew services that run as your own user (not root).
|
||||||
|
|
||||||
|
> **Info**
|
||||||
|
> If your service must run as root, it cannot currently be added to PHP Monitor.
|
||||||
|
|
||||||
|
You can find out which services are available by running `brew services list`.
|
||||||
|
|
||||||
|
Here's an example where we add the `mailhog` and `mysql` services to PHP Monitor:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
"scan_apps": [],
|
||||||
|
"services": ["mailhog", "mysql"],
|
||||||
|
"presets": [],
|
||||||
|
"export": {}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> You must restart PHP Monitor for these changes to be detected.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>How do I set custom environment variables?</strong></summary>
|
||||||
|
|
||||||
|
You must configure these custom environment variables up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||||
|
|
||||||
|
PHP Monitor uses a default Shell environment, with no custom environment variables. You need to set custom environment variables manually. These are then used for e.g. Composer.
|
||||||
|
|
||||||
|
Here's an example of a working `COMPOSER_HOME` environment variable which is respected:
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
{
|
||||||
|
"scan_apps": [],
|
||||||
|
"services": [],
|
||||||
|
"presets": [],
|
||||||
|
"export": {
|
||||||
|
"COMPOSER_HOME": "/absolute/path/to/composer/folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> You must restart PHP Monitor for these changes to be detected.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>How do I get various applications to show up in the domain list's right-click menu?</strong></summary>
|
<summary><strong>How do I get various applications to show up in the domain list's right-click menu?</strong></summary>
|
||||||
|
|
||||||
@@ -308,7 +431,7 @@ All of these apps should just be detected correctly, no matter their location on
|
|||||||
|
|
||||||
To see which files are checked to determine availability, see [this file](./phpmon/Domain/Helpers/Application.swift).
|
To see which files are checked to determine availability, see [this file](./phpmon/Domain/Helpers/Application.swift).
|
||||||
|
|
||||||
You can add your own apps by creating and editing a `~/.phpmon.conf.json` file, with the following entry:
|
You can add your own apps by creating and editing a `~/.config/phpmon/config.json` file, and make sure the `scan_apps` key is set:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
{
|
{
|
||||||
@@ -317,6 +440,9 @@ You can add your own apps by creating and editing a `~/.phpmon.conf.json` file,
|
|||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
|
You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
|
||||||
|
|
||||||
|
> **Warning**
|
||||||
|
> You must restart PHP Monitor for these changes to be detected.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -406,14 +532,14 @@ Donations really help with the Apple Developer Program cost, and keep me motivat
|
|||||||
|
|
||||||
## 😎 Acknowledgements
|
## 😎 Acknowledgements
|
||||||
|
|
||||||
While I did make this application during my own free time, PHP Monitor started out from various learning experiments during work hours at my employer, DIVE. I'd also like to shout out the following folks:
|
Special thanks go out to:
|
||||||
|
|
||||||
* My colleagues at [DIVE](https://dive.be)
|
* Everyone supporting me via [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen)
|
||||||
|
* Everyone who has donated via [my sponsor page](https://nicoverbruggen.be/sponsor)
|
||||||
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
|
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
|
||||||
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
|
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
|
||||||
* My [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen) and those who have donated
|
* Everyone who has left feedback and reported bugs
|
||||||
* Everyone who has left feedback and reported bugs (appreciate it!)
|
* Everyone in the Laravel community who shared the app, especially on Twitter
|
||||||
* Everyone in the Laravel community who shared the app (thanks!)
|
|
||||||
|
|
||||||
Thank you very much for your contributions, kind words and support.
|
Thank you very much for your contributions, kind words and support.
|
||||||
|
|
||||||
@@ -447,7 +573,8 @@ If an extension or other process writes to a single file a bunch of times in a s
|
|||||||
1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name.
|
1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name.
|
||||||
1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all.
|
1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all.
|
||||||
|
|
||||||
*Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly.
|
> **Note**
|
||||||
|
> If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly.
|
||||||
|
|
||||||
### Want to know more?
|
### Want to know more?
|
||||||
|
|
||||||
|
10
SECURITY.md
@@ -6,9 +6,9 @@ Generally speaking, only the latest version of **PHP Monitor** is supported, exc
|
|||||||
|
|
||||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
|
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
|
||||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||||
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 (*) | 3.0 (2.16.2 minimum) |
|
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0)* | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
|
||||||
|
|
||||||
_(*) Support for PHP 5.6 is only included if you are using Valet 2.x, since support for PHP 5.6 was dropped in Valet 3.0._
|
_(*) macOS Ventura (13.0) is not officially supported until it officially releases._
|
||||||
|
|
||||||
## Legacy versions
|
## Legacy versions
|
||||||
|
|
||||||
@@ -16,9 +16,9 @@ These versions of PHP Monitor are no longer supported, but if you’re using an
|
|||||||
|
|
||||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
||||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||||
| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
||||||
| 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
| 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||||
| 3.5 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
| 3.5 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||||
| 3.0—3.4 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.1 | 2.13 |
|
| 3.0—3.4 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.1 | 2.13 |
|
||||||
| 2.6 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.0 | 2.13 |
|
| 2.6 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.0 | 2.13 |
|
||||||
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ | not applicable | not applicable |
|
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ | not applicable | not applicable |
|
||||||
|
BIN
docs/notification-dark.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/screenshot-dark.jpg
Normal file
After Width: | Height: | Size: 454 KiB |
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 469 KiB |
@@ -37,43 +37,43 @@ class NginxConfigurationTest: XCTestCase {
|
|||||||
func testCanDetermineSiteNameAndTld() throws {
|
func testCanDetermineSiteNameAndTld() throws {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
"nginx-site",
|
"nginx-site",
|
||||||
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.domain
|
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.domain
|
||||||
)
|
)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
"test",
|
"test",
|
||||||
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.tld
|
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.tld
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanDetermineIsolation() throws {
|
func testCanDetermineIsolation() throws {
|
||||||
XCTAssertNil(
|
XCTAssertNil(
|
||||||
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.isolatedVersion
|
NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)?.isolatedVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
"8.1",
|
"8.1",
|
||||||
NginxConfiguration.from(filePath: NginxConfigurationTest.isolatedUrl.path)?.isolatedVersion
|
NginxConfigurationFile.from(filePath: NginxConfigurationTest.isolatedUrl.path)?.isolatedVersion
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanDetermineProxy() throws {
|
func testCanDetermineProxy() throws {
|
||||||
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.proxyUrl.path)!
|
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.proxyUrl.path)!
|
||||||
XCTAssertTrue(proxied.contents.contains("# valet stub: proxy.valet.conf"))
|
XCTAssertTrue(proxied.contents.contains("# valet stub: proxy.valet.conf"))
|
||||||
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
||||||
|
|
||||||
let normal = NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)!
|
let normal = NginxConfigurationFile.from(filePath: NginxConfigurationTest.regularUrl.path)!
|
||||||
XCTAssertFalse(normal.contents.contains("# valet stub: proxy.valet.conf"))
|
XCTAssertFalse(normal.contents.contains("# valet stub: proxy.valet.conf"))
|
||||||
XCTAssertEqual(nil, normal.proxy)
|
XCTAssertEqual(nil, normal.proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanDetermineSecuredProxy() throws {
|
func testCanDetermineSecuredProxy() throws {
|
||||||
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.secureProxyUrl.path)!
|
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.secureProxyUrl.path)!
|
||||||
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
||||||
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanDetermineProxyWithCustomTld() throws {
|
func testCanDetermineProxyWithCustomTld() throws {
|
||||||
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.customTldProxyUrl.path)!
|
let proxied = NginxConfigurationFile.from(filePath: NginxConfigurationTest.customTldProxyUrl.path)!
|
||||||
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
||||||
XCTAssertEqual("http://localhost:8080", proxied.proxy)
|
XCTAssertEqual("http://localhost:8080", proxied.proxy)
|
||||||
}
|
}
|
||||||
|
84
phpmon-tests/Parsers/PhpConfigurationTest.swift
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// PhpConfigurationTest.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 04/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class PhpConfigurationTest: XCTestCase {
|
||||||
|
|
||||||
|
static var phpIniFileUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "php", withExtension: "ini")!
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanLoadExtension() throws {
|
||||||
|
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(iniFile)
|
||||||
|
|
||||||
|
XCTAssertGreaterThan(iniFile.extensions.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanCheckKeyExistence() throws {
|
||||||
|
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
|
||||||
|
|
||||||
|
XCTAssertTrue(iniFile.has(key: "error_reporting"))
|
||||||
|
XCTAssertTrue(iniFile.has(key: "display_errors"))
|
||||||
|
XCTAssertFalse(iniFile.has(key: "my_unknown_key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanCheckKeyValue() throws {
|
||||||
|
let iniFile = PhpConfigurationFile.from(filePath: Self.phpIniFileUrl.path)!
|
||||||
|
|
||||||
|
XCTAssertNotNil(iniFile.get(for: "error_reporting"))
|
||||||
|
XCTAssert(iniFile.get(for: "error_reporting") == "E_ALL")
|
||||||
|
|
||||||
|
XCTAssertNotNil(iniFile.get(for: "display_errors"))
|
||||||
|
XCTAssert(iniFile.get(for: "display_errors") == "On")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanCustomizeConfigurationValue() throws {
|
||||||
|
let destination = Utility
|
||||||
|
.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||||
|
|
||||||
|
let configurationFile = PhpConfigurationFile
|
||||||
|
.from(filePath: destination.path)!
|
||||||
|
|
||||||
|
// 0. Verify the original value
|
||||||
|
XCTAssertEqual(configurationFile.get(for: "error_reporting"), "E_ALL")
|
||||||
|
|
||||||
|
// 1. Change the value
|
||||||
|
try! configurationFile.replace(
|
||||||
|
key: "error_reporting",
|
||||||
|
value: "E_ALL & ~E_DEPRECATED & ~E_STRICT"
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
configurationFile.get(for: "error_reporting"),
|
||||||
|
"E_ALL & ~E_DEPRECATED & ~E_STRICT"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2. Ensure that same key and value doesn't break subsequent saves
|
||||||
|
try! configurationFile.replace(
|
||||||
|
key: "error_reporting",
|
||||||
|
value: "error_reporting"
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
configurationFile.get(for: "error_reporting"),
|
||||||
|
"error_reporting"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 3. Verify subsequent saves weren't broken
|
||||||
|
try! configurationFile.replace(
|
||||||
|
key: "error_reporting",
|
||||||
|
value: "E_ALL"
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
configurationFile.get(for: "error_reporting"),
|
||||||
|
"E_ALL"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -15,13 +15,13 @@ class PhpExtensionTest: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testCanLoadExtension() throws {
|
func testCanLoadExtension() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||||
|
|
||||||
XCTAssertGreaterThan(extensions.count, 0)
|
XCTAssertGreaterThan(extensions.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExtensionNameIsCorrect() throws {
|
func testExtensionNameIsCorrect() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||||
|
|
||||||
let extensionNames = extensions.map { (ext) -> String in
|
let extensionNames = extensions.map { (ext) -> String in
|
||||||
return ext.name
|
return ext.name
|
||||||
@@ -40,7 +40,7 @@ class PhpExtensionTest: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testExtensionStatusIsCorrect() throws {
|
func testExtensionStatusIsCorrect() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.from(filePath: Self.phpIniFileUrl.path)
|
||||||
|
|
||||||
// xdebug should be enabled
|
// xdebug should be enabled
|
||||||
XCTAssertEqual(extensions[0].enabled, true)
|
XCTAssertEqual(extensions[0].enabled, true)
|
||||||
@@ -51,7 +51,7 @@ class PhpExtensionTest: XCTestCase {
|
|||||||
|
|
||||||
func testToggleWorksAsExpected() throws {
|
func testToggleWorksAsExpected() throws {
|
||||||
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||||
let extensions = PhpExtension.load(from: destination)
|
let extensions = PhpExtension.from(filePath: destination.path)
|
||||||
XCTAssertEqual(extensions.count, 6)
|
XCTAssertEqual(extensions.count, 6)
|
||||||
|
|
||||||
// Try to disable xdebug (should be detected first)!
|
// Try to disable xdebug (should be detected first)!
|
||||||
@@ -66,12 +66,7 @@ class PhpExtensionTest: XCTestCase {
|
|||||||
XCTAssertTrue(file.contains("; zend_extension=\"xdebug.so\""))
|
XCTAssertTrue(file.contains("; zend_extension=\"xdebug.so\""))
|
||||||
|
|
||||||
// Make sure if we load the data again, it's disabled
|
// Make sure if we load the data again, it's disabled
|
||||||
XCTAssertEqual(PhpExtension.load(from: destination).first!.enabled, false)
|
XCTAssertEqual(PhpExtension.from(filePath: destination.path).first!.enabled, false)
|
||||||
}
|
|
||||||
|
|
||||||
func testCanRetrieveXdebugMode() throws {
|
|
||||||
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('xdebug.mode');"])
|
|
||||||
XCTAssertEqual(value, "coverage")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
34
phpmon-tests/Test Files/phpmon/phpmon-config.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"scan_apps": [],
|
||||||
|
"presets": [
|
||||||
|
{
|
||||||
|
"name": "Default PHP",
|
||||||
|
"extensions": {
|
||||||
|
"xdebug": false
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"memory_limit": "128M"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Personal Site",
|
||||||
|
"extensions": {
|
||||||
|
"xdebug": true
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"xdebug.mode": "coverage",
|
||||||
|
"memory_limit": "512M"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PHP Monitor",
|
||||||
|
"extensions": {
|
||||||
|
"xdebug": true
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"xdebug.mode": "coverage",
|
||||||
|
"memory_limit": "512M"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
38
phpmon/Assets.xcassets/AppColor.colorset/Contents.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.988",
|
||||||
|
"green" : "0.580",
|
||||||
|
"red" : "0.277"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.988",
|
||||||
|
"green" : "0.723",
|
||||||
|
"red" : "0.277"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
38
phpmon/Assets.xcassets/AppSecondary.colorset/Contents.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.250",
|
||||||
|
"green" : "0.250",
|
||||||
|
"red" : "0.250"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.750",
|
||||||
|
"green" : "0.750",
|
||||||
|
"red" : "0.750"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ServiceLoading.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "ServiceLoading@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"template-rendering-intent" : "template"
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 854 B |
Before Width: | Height: | Size: 1.3 KiB |
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ServiceOff.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "ServiceOff@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"template-rendering-intent" : "template"
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 826 B |
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"filename" : "ServiceOn.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"filename" : "ServiceOn@2x.png",
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"author" : "xcode",
|
|
||||||
"version" : 1
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"template-rendering-intent" : "template"
|
|
||||||
}
|
|
||||||
}
|
|
Before Width: | Height: | Size: 819 B |
Before Width: | Height: | Size: 1.2 KiB |
@@ -24,7 +24,7 @@ class Actions {
|
|||||||
brew("services restart dnsmasq", sudo: true)
|
brew("services restart dnsmasq", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func stopAllServices() {
|
public static func stopValetServices() {
|
||||||
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
|
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
|
||||||
brew("services stop nginx", sudo: true)
|
brew("services stop nginx", sudo: true)
|
||||||
brew("services stop dnsmasq", sudo: true)
|
brew("services stop dnsmasq", sudo: true)
|
||||||
@@ -64,6 +64,29 @@ class Actions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Third Party Services
|
||||||
|
public static func stopService(name: String, completion: @escaping () -> Void) {
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
brew("services stop \(name)", sudo: ServicesManager.shared.rootServices.contains { $0.value.name == name })
|
||||||
|
ServicesManager.loadHomebrewServices(completed: {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func startService(name: String, completion: @escaping () -> Void) {
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
brew("services start \(name)", sudo: ServicesManager.shared.rootServices.contains { $0.value.name == name })
|
||||||
|
ServicesManager.loadHomebrewServices(completed: {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Finding Config Files
|
// MARK: - Finding Config Files
|
||||||
|
|
||||||
public static func openGenericPhpConfigFolder() {
|
public static func openGenericPhpConfigFolder() {
|
||||||
@@ -88,6 +111,12 @@ class Actions {
|
|||||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func openPhpMonitorConfigFile() {
|
||||||
|
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||||
|
.appendingPathComponent(".config/phpmon")
|
||||||
|
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Other Actions
|
// MARK: - Other Actions
|
||||||
|
|
||||||
public static func createTempPhpInfoFile() -> URL {
|
public static func createTempPhpInfoFile() -> URL {
|
||||||
|
@@ -21,7 +21,7 @@ public class Paths {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
||||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
userName = String(Shell.pipe("id -un").split(separator: "\n")[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func detectBinaryPaths() {
|
public func detectBinaryPaths() {
|
||||||
@@ -57,6 +57,10 @@ public class Paths {
|
|||||||
return shared.userName
|
return shared.userName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var homePath: String {
|
||||||
|
return NSHomeDirectory()
|
||||||
|
}
|
||||||
|
|
||||||
public static var cellarPath: String {
|
public static var cellarPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/Cellar"
|
return "\(shared.baseDir.rawValue)/Cellar"
|
||||||
}
|
}
|
||||||
|
27
phpmon/Common/Core/Shell+PATH.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// Shell+PATH.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 15/08/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Shell {
|
||||||
|
|
||||||
|
var PATH: String {
|
||||||
|
let task = Process()
|
||||||
|
task.launchPath = "/bin/zsh"
|
||||||
|
|
||||||
|
// We need an interactive shell so the user's PATH is loaded in correctly
|
||||||
|
task.arguments = ["--login", "-ilc", "echo $PATH"]
|
||||||
|
|
||||||
|
let pipe = Pipe()
|
||||||
|
task.standardOutput = pipe
|
||||||
|
task.launch()
|
||||||
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
|
|
||||||
|
return String(data: data, encoding: String.Encoding.utf8) ?? ""
|
||||||
|
}
|
||||||
|
}
|
@@ -32,6 +32,9 @@ public class Shell {
|
|||||||
*/
|
*/
|
||||||
public var shell: String = "/bin/sh"
|
public var shell: String = "/bin/sh"
|
||||||
|
|
||||||
|
/** Additional exports that are sent if `requiresPath` is set to true. */
|
||||||
|
public var exports: String = ""
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Singleton to access a user shell (with --login)
|
Singleton to access a user shell (with --login)
|
||||||
*/
|
*/
|
||||||
@@ -114,13 +117,23 @@ public class Shell {
|
|||||||
Creates a new process with the correct PATH and shell.
|
Creates a new process with the correct PATH and shell.
|
||||||
*/
|
*/
|
||||||
public func createTask(for command: String, requiresPath: Bool) -> Process {
|
public func createTask(for command: String, requiresPath: Bool) -> Process {
|
||||||
let tailoredCommand = requiresPath
|
var completeCommand = ""
|
||||||
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
|
||||||
: command
|
if requiresPath {
|
||||||
|
// Basic export (PATH)
|
||||||
|
completeCommand += "export PATH=\(Paths.binPath):$PATH && "
|
||||||
|
|
||||||
|
// Put additional exports in between
|
||||||
|
if !self.exports.isEmpty {
|
||||||
|
completeCommand += "\(self.exports) && "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completeCommand += command
|
||||||
|
|
||||||
let task = Process()
|
let task = Process()
|
||||||
task.launchPath = self.shell
|
task.launchPath = self.shell
|
||||||
task.arguments = ["--noprofile", "-norc", "--login", "-c", tailoredCommand]
|
task.arguments = ["--noprofile", "-norc", "--login", "-c", completeCommand]
|
||||||
|
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
24
phpmon/Common/Extensions/ArrayExtension.swift
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// ArrayExtension.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 11/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
/**
|
||||||
|
Sourced from Stack Overflow
|
||||||
|
https://stackoverflow.com/a/33540708
|
||||||
|
*/
|
||||||
|
func chunked(by distance: Int) -> [[Element]] {
|
||||||
|
let indicesSequence = stride(from: startIndex, to: endIndex, by: distance)
|
||||||
|
let array: [[Element]] = indicesSequence.map {
|
||||||
|
let newIndex = $0.advanced(by: distance) > endIndex ? endIndex : $0.advanced(by: distance)
|
||||||
|
return Array(self[$0 ..< newIndex])
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
}
|
@@ -27,3 +27,25 @@ extension NSMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - NSMenuItem subclasses
|
||||||
|
|
||||||
|
class PhpMenuItem: NSMenuItem {
|
||||||
|
var version: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class XdebugMenuItem: NSMenuItem {
|
||||||
|
var mode: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtensionMenuItem: NSMenuItem {
|
||||||
|
var phpExtension: PhpExtension?
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditorMenuItem: NSMenuItem {
|
||||||
|
var editor: Application?
|
||||||
|
}
|
||||||
|
|
||||||
|
class PresetMenuItem: NSMenuItem {
|
||||||
|
var preset: Preset?
|
||||||
|
}
|
||||||
|
@@ -11,6 +11,20 @@ import Cocoa
|
|||||||
|
|
||||||
extension NSWindow {
|
extension NSWindow {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Centers a window. Taken from: https://stackoverflow.com/a/66140320
|
||||||
|
*/
|
||||||
|
public func setCenterPosition(offsetY: CGFloat = 0) {
|
||||||
|
if let screenSize = screen?.visibleFrame.size {
|
||||||
|
self.setFrameOrigin(
|
||||||
|
NSPoint(
|
||||||
|
x: (screenSize.width - frame.size.width) / 2,
|
||||||
|
y: (screenSize.height - frame.size.height) / 2 + offsetY
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Shakes a window. Inspired by: http://blog.ericd.net/2016/09/30/shaking-a-macos-window/
|
Shakes a window. Inspired by: http://blog.ericd.net/2016/09/30/shaking-a-macos-window/
|
||||||
*/
|
*/
|
||||||
|
@@ -5,13 +5,23 @@
|
|||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
var localized: String {
|
var localized: String {
|
||||||
|
if #available(macOS 13, *) {
|
||||||
|
return NSLocalizedString(
|
||||||
|
self, tableName: nil, bundle: Bundle.main, value: "", comment: ""
|
||||||
|
).replacingOccurrences(of: "Preferences", with: "Settings")
|
||||||
|
}
|
||||||
|
|
||||||
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var localizedForSwiftUI: LocalizedStringKey {
|
||||||
|
return LocalizedStringKey(self.localized)
|
||||||
|
}
|
||||||
|
|
||||||
func localized(_ args: CVarArg...) -> String {
|
func localized(_ args: CVarArg...) -> String {
|
||||||
String(format: self.localized, arguments: args)
|
String(format: self.localized, arguments: args)
|
||||||
}
|
}
|
||||||
@@ -32,7 +42,7 @@ extension String {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
subscript (r: Range<String.Index>) -> String {
|
subscript(r: Range<String.Index>) -> String {
|
||||||
let start = r.lowerBound
|
let start = r.lowerBound
|
||||||
let end = r.upperBound
|
let end = r.upperBound
|
||||||
return String(self[start ..< end])
|
return String(self[start ..< end])
|
||||||
@@ -71,4 +81,22 @@ extension String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stripped: String {
|
||||||
|
do {
|
||||||
|
guard let data = self.data(using: .unicode) else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
let attributed = try NSAttributedString(
|
||||||
|
data: data,
|
||||||
|
options: [
|
||||||
|
.documentType: NSAttributedString.DocumentType.html,
|
||||||
|
.characterEncoding: String.Encoding.utf8.rawValue],
|
||||||
|
documentAttributes: nil
|
||||||
|
)
|
||||||
|
return attributed.string
|
||||||
|
} catch {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// FileSystem.swift
|
// Filesystem.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 07/12/2021.
|
// Created by Nico Verbruggen on 07/12/2021.
|
||||||
@@ -7,17 +7,55 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
import Foundation
|
||||||
|
|
||||||
class Filesystem {
|
class Filesystem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a file exists at the provided path.
|
Checks if a file or directory exists at the provided path.
|
||||||
Uses `FileManager`.
|
|
||||||
*/
|
*/
|
||||||
public static func fileExists(_ path: String) -> Bool {
|
public static func exists(_ path: String) -> Bool {
|
||||||
return FileManager.default.fileExists(
|
return FileManager.default.fileExists(
|
||||||
atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
atPath: path.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if a file exists at the provided path.
|
||||||
|
*/
|
||||||
|
public static func fileExists(_ path: String) -> Bool {
|
||||||
|
var isDirectory: ObjCBool = true
|
||||||
|
let exists = FileManager.default.fileExists(
|
||||||
|
atPath: path.replacingOccurrences(of: "~", with: Paths.homePath),
|
||||||
|
isDirectory: &isDirectory
|
||||||
|
)
|
||||||
|
|
||||||
|
return exists && !isDirectory.boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if a directory exists at the provided path.
|
||||||
|
*/
|
||||||
|
public static func directoryExists(_ path: String) -> Bool {
|
||||||
|
var isDirectory: ObjCBool = true
|
||||||
|
let exists = FileManager.default.fileExists(
|
||||||
|
atPath: path.replacingOccurrences(of: "~", with: Paths.homePath),
|
||||||
|
isDirectory: &isDirectory
|
||||||
|
)
|
||||||
|
|
||||||
|
return exists && isDirectory.boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if a given file is a symbolic link.
|
||||||
|
*/
|
||||||
|
public static func fileIsSymlink(_ path: String) -> Bool {
|
||||||
|
do {
|
||||||
|
let attribs = try FileManager.default.attributesOfItem(atPath: path)
|
||||||
|
return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,11 @@ import UserNotifications
|
|||||||
|
|
||||||
class LocalNotification {
|
class LocalNotification {
|
||||||
|
|
||||||
public static func send(title: String, subtitle: String) {
|
@MainActor public static func send(title: String, subtitle: String, preference: PreferenceName) {
|
||||||
|
if !Preferences.isEnabled(preference) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = title
|
content.title = title
|
||||||
content.body = subtitle
|
content.body = subtitle
|
||||||
|
@@ -20,7 +20,13 @@ class ActivePhpInstallation {
|
|||||||
|
|
||||||
var version: Version!
|
var version: Version!
|
||||||
var limits: Limits!
|
var limits: Limits!
|
||||||
var extensions: [PhpExtension]!
|
var iniFiles: [PhpConfigurationFile] = []
|
||||||
|
|
||||||
|
var extensions: [PhpExtension] {
|
||||||
|
return iniFiles.flatMap { initFile in
|
||||||
|
return initFile.extensions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Computed
|
// MARK: - Computed
|
||||||
|
|
||||||
@@ -34,16 +40,21 @@ class ActivePhpInstallation {
|
|||||||
// Show information about the current version
|
// Show information about the current version
|
||||||
getVersion()
|
getVersion()
|
||||||
|
|
||||||
|
// Initialize the list of ini files that are loaded
|
||||||
|
iniFiles = []
|
||||||
|
|
||||||
// If an error occurred, exit early
|
// If an error occurred, exit early
|
||||||
if version.error {
|
if version.error {
|
||||||
limits = Limits()
|
limits = Limits()
|
||||||
extensions = []
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load extension information
|
// Load extension information
|
||||||
let path = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
|
let mainConfigurationFileUrl = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
|
||||||
extensions = PhpExtension.load(from: path)
|
|
||||||
|
if let file = PhpConfigurationFile.from(filePath: mainConfigurationFileUrl.path) {
|
||||||
|
iniFiles.append(file)
|
||||||
|
}
|
||||||
|
|
||||||
// Get configuration values
|
// Get configuration values
|
||||||
limits = Limits(
|
limits = Limits(
|
||||||
@@ -60,9 +71,8 @@ class ActivePhpInstallation {
|
|||||||
|
|
||||||
// See if any extensions are present in said .ini files
|
// See if any extensions are present in said .ini files
|
||||||
paths.forEach { (iniFilePath) in
|
paths.forEach { (iniFilePath) in
|
||||||
let loadedExtensions = PhpExtension.load(from: URL(fileURLWithPath: iniFilePath))
|
if let file = PhpConfigurationFile.from(filePath: iniFilePath) {
|
||||||
if !loadedExtensions.isEmpty {
|
iniFiles.append(file)
|
||||||
extensions.append(contentsOf: loadedExtensions)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,16 +11,23 @@ import Foundation
|
|||||||
class Xdebug {
|
class Xdebug {
|
||||||
|
|
||||||
public static var enabled: Bool {
|
public static var enabled: Bool {
|
||||||
return !self.mode.isEmpty
|
return PhpEnv.shared.getConfigFile(forKey: "xdebug.mode") != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var mode: String {
|
public static var activeModes: [String] {
|
||||||
return Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('xdebug.mode');"])
|
guard let file = PhpEnv.shared.getConfigFile(forKey: "xdebug.mode") else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let value = file.get(for: "xdebug.mode") else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.components(separatedBy: ",").filter { self.modes.contains($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var modes: [String] {
|
public static var modes: [String] {
|
||||||
return [
|
return [
|
||||||
"off",
|
|
||||||
"develop",
|
"develop",
|
||||||
"coverage",
|
"coverage",
|
||||||
"debug",
|
"debug",
|
||||||
|
@@ -19,20 +19,20 @@ struct HomebrewService: Decodable, Equatable {
|
|||||||
let log_path: String?
|
let log_path: String?
|
||||||
let error_log_path: String?
|
let error_log_path: String?
|
||||||
|
|
||||||
public static func loadAll(
|
/**
|
||||||
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"],
|
Dummy data for preview purposes.
|
||||||
completion: @escaping ([HomebrewService]) -> Void
|
*/
|
||||||
) {
|
public static func dummy(named service: String, enabled: Bool) -> Self {
|
||||||
DispatchQueue.global(qos: .background).async {
|
return HomebrewService(
|
||||||
let data = Shell
|
name: service,
|
||||||
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
service_name: service,
|
||||||
.data(using: .utf8)!
|
running: enabled,
|
||||||
|
loaded: enabled,
|
||||||
let services = try! JSONDecoder()
|
pid: nil,
|
||||||
.decode([HomebrewService].self, from: data)
|
user: nil,
|
||||||
.filter({ return filter.contains($0.name) })
|
status: nil,
|
||||||
|
log_path: nil,
|
||||||
completion(services)
|
error_log_path: nil
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -174,4 +174,14 @@ class PhpEnv {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the configuration file instance that is used for a specific config value.
|
||||||
|
You can then use the configuration file instance to change values.
|
||||||
|
*/
|
||||||
|
public func getConfigFile(forKey key: String) -> PhpConfigurationFile? {
|
||||||
|
return PhpEnv.phpInstall.iniFiles
|
||||||
|
.reversed()
|
||||||
|
.first(where: { $0.has(key: key) })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,8 +16,18 @@ class PhpHelper {
|
|||||||
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||||
let dotless = version.replacingOccurrences(of: ".", with: "")
|
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
|
// Determine the dotless name for this PHP version
|
||||||
|
let destination = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||||
|
|
||||||
|
// Check if the ~/.config/phpmon/bin directory is in the PATH
|
||||||
|
let inPath = Shell.user.PATH.contains("\(Paths.homePath)/.config/phpmon/bin")
|
||||||
|
|
||||||
|
// Check if we can create symlinks (`/usr/local/bin` must be writable)
|
||||||
|
let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let destination = "/usr/local/bin/pm\(dotless)"
|
Shell.run("mkdir -p ~/.config/phpmon/bin")
|
||||||
|
|
||||||
if FileManager.default.fileExists(atPath: destination) {
|
if FileManager.default.fileExists(atPath: destination) {
|
||||||
let contents = try String(contentsOfFile: destination)
|
let contents = try String(contentsOfFile: destination)
|
||||||
if !contents.contains(keyPhrase) {
|
if !contents.contains(keyPhrase) {
|
||||||
@@ -52,10 +62,40 @@ class PhpHelper {
|
|||||||
|
|
||||||
// Make sure the file is executable
|
// Make sure the file is executable
|
||||||
Shell.run("chmod +x \(destination)")
|
Shell.run("chmod +x \(destination)")
|
||||||
|
|
||||||
|
// Create a symlink if the folder is not in the PATH
|
||||||
|
if !inPath {
|
||||||
|
// First, check if we can create symlinks at all
|
||||||
|
if !canWriteSymlinks {
|
||||||
|
Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the symlink
|
||||||
|
self.createSymlink(dotless)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print(error)
|
print(error)
|
||||||
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func createSymlink(_ dotless: String) {
|
||||||
|
let source = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
|
||||||
|
let destination = "/usr/local/bin/pm\(dotless)"
|
||||||
|
|
||||||
|
if !Filesystem.fileExists(destination) {
|
||||||
|
Log.info("Creating new symlink: \(destination)")
|
||||||
|
Shell.run("ln -s \(source) \(destination)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Filesystem.fileIsSymlink(destination) {
|
||||||
|
Log.info("Overwriting existing file with new symlink: \(destination)")
|
||||||
|
Shell.run("ln -fs \(source) \(destination)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("Symlink in \(destination) already exists, OK.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -91,7 +91,7 @@ public struct PhpVersionNumberCollection: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PhpVersionNumber: Equatable {
|
public struct PhpVersionNumber: Equatable, Hashable {
|
||||||
let major: Int
|
let major: Int
|
||||||
let minor: Int
|
let minor: Int
|
||||||
let patch: Int?
|
let patch: Int?
|
||||||
|
218
phpmon/Common/PHP/PhpConfigurationFile.swift
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
//
|
||||||
|
// PhpConfigurationFile.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 04/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpConfigurationFile: CreatedFromFile {
|
||||||
|
|
||||||
|
struct ConfigValue {
|
||||||
|
let lineIndex: Int
|
||||||
|
let value: String
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Section = [String: ConfigValue]
|
||||||
|
typealias Config = [String: Section]
|
||||||
|
|
||||||
|
/// The file where this configuration file was located.
|
||||||
|
let filePath: String
|
||||||
|
|
||||||
|
/// The extensions found in this .ini file.
|
||||||
|
var extensions: [PhpExtension]
|
||||||
|
|
||||||
|
/// The actual, structured content of the configuration file.
|
||||||
|
var content: Config
|
||||||
|
|
||||||
|
/// The original lines of the file.
|
||||||
|
var lines: [String]
|
||||||
|
|
||||||
|
/** Resolves a PHP configuration file (.ini) */
|
||||||
|
static func from(filePath: String) -> Self? {
|
||||||
|
let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let fileContents = try String(contentsOfFile: path)
|
||||||
|
return Self.init(path: path, contents: fileContents)
|
||||||
|
} catch {
|
||||||
|
Log.warn("Could not read the PHP configuration file at: `\(filePath)`")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(path: String, contents: String) {
|
||||||
|
self.filePath = path
|
||||||
|
self.lines = contents.components(separatedBy: "\n")
|
||||||
|
self.extensions = PhpExtension.from(lines, filePath: path)
|
||||||
|
self.content = Self.parseConfig(lines: lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: API
|
||||||
|
|
||||||
|
public func has(key: String) -> Bool {
|
||||||
|
return self.content.contains { (_: String, section: Section) in
|
||||||
|
return section.keys.contains(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func get(for key: String) -> String? {
|
||||||
|
return getConfig(for: key)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getConfig(for key: String) -> ConfigValue? {
|
||||||
|
for (_, section) in self.content {
|
||||||
|
if section.keys.contains(key) {
|
||||||
|
return section[key]!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ReplacementErrors: Error {
|
||||||
|
case missingKey
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Replaces the value for a specific (existing) key with a new value.
|
||||||
|
The key must exist for this to work.
|
||||||
|
*/
|
||||||
|
public func replace(key: String, value: String) throws {
|
||||||
|
// Ensure that the key exists
|
||||||
|
guard let item = getConfig(for: key) else {
|
||||||
|
throw ReplacementErrors.missingKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out what comes after the assignment
|
||||||
|
var components = self
|
||||||
|
.lines[item.lineIndex]
|
||||||
|
.components(separatedBy: "=")
|
||||||
|
|
||||||
|
// Replace the value with the new one
|
||||||
|
components[1] = components[1]
|
||||||
|
.replacingOccurrences(of: item.value, with: value)
|
||||||
|
|
||||||
|
// Replace the specific line
|
||||||
|
self.lines[item.lineIndex] = components.joined(separator: "=")
|
||||||
|
|
||||||
|
// Finally, join the string and save the file atomatically again
|
||||||
|
try self.lines.joined(separator: "\n")
|
||||||
|
.write(toFile: self.filePath, atomically: true, encoding: .utf8)
|
||||||
|
|
||||||
|
// Reload the original file
|
||||||
|
self.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func reload() {
|
||||||
|
self.lines = try! String(contentsOfFile: self.filePath)
|
||||||
|
.components(separatedBy: "\n")
|
||||||
|
self.extensions = PhpExtension.from(lines, filePath: self.filePath)
|
||||||
|
self.content = Self.parseConfig(lines: lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Parsing Logic
|
||||||
|
// Slightly modified from: https://gist.github.com/jetmind/f776c0d223e4ac6aec1ff9389e874553
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attempts to parse the configuration file, based on an array of strings.
|
||||||
|
Each string is a line from the configuration file.
|
||||||
|
*/
|
||||||
|
private static func parseConfig(lines: [String]) -> Config {
|
||||||
|
var config = Config()
|
||||||
|
|
||||||
|
var currentSectionName = "main"
|
||||||
|
|
||||||
|
for (index, line) in lines.enumerated() {
|
||||||
|
let line = trim(line)
|
||||||
|
|
||||||
|
if line.hasPrefix("[") && line.hasSuffix("]") {
|
||||||
|
currentSectionName = parseSectionHeader(line)
|
||||||
|
} else if let (key, value) = parseLine(line) {
|
||||||
|
var section = config[currentSectionName] ?? [:]
|
||||||
|
section[key] = ConfigValue(
|
||||||
|
lineIndex: index,
|
||||||
|
value: value
|
||||||
|
)
|
||||||
|
config[currentSectionName] = section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove all whitespace and additional characters from individual lines.
|
||||||
|
*/
|
||||||
|
private static func trim(_ string: String) -> String {
|
||||||
|
let whitespaces = CharacterSet(charactersIn: " \n\r\t")
|
||||||
|
return string.trimmingCharacters(in: whitespaces)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
It may prove beneficial to strip all comments, which can start with # or ;.
|
||||||
|
In this case, strip both.
|
||||||
|
*/
|
||||||
|
private static func stripComment(_ line: String) -> String {
|
||||||
|
var line = line
|
||||||
|
|
||||||
|
let characters: [String.Element] = ["#", ";"]
|
||||||
|
|
||||||
|
for character in characters {
|
||||||
|
// Only keep checking for comments as long as the line isn't empty
|
||||||
|
if line.isEmpty {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the next comment character
|
||||||
|
line = strip(character: character, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Empties a line if it happens to be commented out, causing it to be ignored.
|
||||||
|
*/
|
||||||
|
private static func strip(character: String.Element, _ line: String) -> String {
|
||||||
|
let parts = line.split(
|
||||||
|
separator: character,
|
||||||
|
maxSplits: 1,
|
||||||
|
omittingEmptySubsequences: false
|
||||||
|
)
|
||||||
|
|
||||||
|
if !parts.isEmpty {
|
||||||
|
return String(parts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attempts to parse a section header. Requires the line to start with [ and end with ].
|
||||||
|
*/
|
||||||
|
private static func parseSectionHeader(_ line: String) -> String {
|
||||||
|
let from = line.index(after: line.startIndex)
|
||||||
|
let to = line.index(before: line.endIndex)
|
||||||
|
|
||||||
|
return line[from..<to]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attempts to parse a regular line, which may contain a configuration value that is being set.
|
||||||
|
*/
|
||||||
|
private static func parseLine(_ line: String) -> (String, String)? {
|
||||||
|
let parts = stripComment(line)
|
||||||
|
.split(separator: "=", maxSplits: 1)
|
||||||
|
|
||||||
|
if parts.count == 2 {
|
||||||
|
let k = trim(String(parts[0]))
|
||||||
|
let v = trim(String(parts[1]))
|
||||||
|
return (k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -89,24 +89,26 @@ class PhpExtension {
|
|||||||
|
|
||||||
// MARK: - Static Methods
|
// MARK: - Static Methods
|
||||||
|
|
||||||
/**
|
static func from(_ lines: [String], filePath: String) -> [PhpExtension] {
|
||||||
This method will attempt to identify all extensions in the .ini file at a certain URL.
|
return lines.filter {
|
||||||
*/
|
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
||||||
static func load(from path: URL) -> [PhpExtension] {
|
}.map {
|
||||||
let file = try? String(contentsOf: path, encoding: .utf8)
|
return PhpExtension($0, file: filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func from(filePath: String) -> [PhpExtension] {
|
||||||
|
let file = try? String(contentsOfFile: filePath)
|
||||||
|
|
||||||
if file == nil {
|
if file == nil {
|
||||||
Log.err("There was an issue reading the file. Assuming no extensions were found.")
|
Log.err("There was an issue reading the file. Assuming no extensions were found.")
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return file!.components(separatedBy: "\n")
|
return Self.from(
|
||||||
.filter {
|
file!.components(separatedBy: "\n"),
|
||||||
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
filePath: filePath
|
||||||
}
|
)
|
||||||
.map {
|
|
||||||
return PhpExtension($0, file: path.path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -23,17 +23,7 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
func performSwitch(to version: String, completion: @escaping () -> Void) {
|
func performSwitch(to version: String, completion: @escaping () -> Void) {
|
||||||
Log.info("Switching to \(version), unlinking all versions...")
|
Log.info("Switching to \(version), unlinking all versions...")
|
||||||
|
|
||||||
let isolated = Valet.shared.sites.filter { site in
|
let versions = getVersionsToBeHandled(version)
|
||||||
site.isolatedPhpVersion != nil
|
|
||||||
}.map { site in
|
|
||||||
return site.isolatedPhpVersion!.versionNumber.homebrewVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
var versions: Set<String> = [version]
|
|
||||||
|
|
||||||
if Valet.enabled(feature: .isolatedSites) {
|
|
||||||
versions = versions.union(isolated)
|
|
||||||
}
|
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
@@ -63,7 +53,28 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func disableDefaultPhpFpmPool(_ version: String) {
|
func getVersionsToBeHandled(_ primary: String) -> Set<String> {
|
||||||
|
let isolated = Valet.shared.sites.filter { site in
|
||||||
|
site.isolatedPhpVersion != nil
|
||||||
|
}.map { site in
|
||||||
|
return site.isolatedPhpVersion!.versionNumber.homebrewVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
var versions: Set<String> = [primary]
|
||||||
|
|
||||||
|
if Valet.enabled(feature: .isolatedSites) {
|
||||||
|
versions = versions.union(isolated)
|
||||||
|
}
|
||||||
|
|
||||||
|
return versions
|
||||||
|
}
|
||||||
|
|
||||||
|
func requiresDisablingOfDefaultPhpFpmPool(_ version: String) -> Bool {
|
||||||
|
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||||
|
return FileManager.default.fileExists(atPath: pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableDefaultPhpFpmPool(_ version: String) {
|
||||||
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||||
if FileManager.default.fileExists(atPath: pool) {
|
if FileManager.default.fileExists(atPath: pool) {
|
||||||
Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).")
|
Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).")
|
||||||
@@ -83,14 +94,14 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopPhpVersion(_ version: String) {
|
func stopPhpVersion(_ version: String) {
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
brew("unlink \(formula)")
|
brew("unlink \(formula)")
|
||||||
brew("services stop \(formula)", sudo: true)
|
brew("services stop \(formula)", sudo: true)
|
||||||
Log.info("Unlinked and stopped services for \(formula)")
|
Log.info("Unlinked and stopped services for \(formula)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startPhpVersion(_ version: String, primary: Bool) {
|
func startPhpVersion(_ version: String, primary: Bool) {
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
|
||||||
if primary {
|
if primary {
|
||||||
|
15
phpmon/Common/Protocols/CreatedFromFile.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// CreatedFromFile.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 15/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol CreatedFromFile {
|
||||||
|
|
||||||
|
static func from(filePath: String) -> Self?
|
||||||
|
|
||||||
|
}
|
@@ -13,9 +13,9 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<br>
|
<br>
|
||||||
<p><b>Want to spread the love?</b> Leave a <a href="https://github.com/nicoverbruggen/phpmon">star on GitHub</a>!</p>
|
<p><b>Do you enjoy using the app?</b> Leave a <a href="https://github.com/nicoverbruggen/phpmon">star on GitHub</a>!</p>
|
||||||
<p><b>Having issues?</b> Consult the <a href="https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting">FAQ & Troubleshooting</a> section.</p>
|
<p><b>Having issues?</b> Consult the <a href="https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting">FAQ</a> section, I did my best to ensure everything is documented.</p>
|
||||||
<p><b>Want to support me?</b> You can <a href="https://nicoverbruggen.be/sponsor">financially support</a> the continued development of this app.</p>
|
<p><b>Want to support further development of PHP Monitor?</b> You can <a href="https://nicoverbruggen.be/sponsor">financially support</a> the continued development of this app.</p>
|
||||||
<p><b>Get the latest on Twitter</b> Give me a <a href="https://twitter.com/nicoverbruggen">follow on Twitter</a> to learn about the latest and greatest updates of this app.</p>
|
<p><b>Get the latest on Twitter</b> Give me a <a href="https://twitter.com/nicoverbruggen">follow on Twitter</a> to learn about the latest and greatest updates of this app.</p>
|
||||||
<br>
|
<br>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -51,14 +51,26 @@ class App {
|
|||||||
var preferences: [PreferenceName: Bool]!
|
var preferences: [PreferenceName: Bool]!
|
||||||
|
|
||||||
/** The window controller of the currently active preferences window. */
|
/** The window controller of the currently active preferences window. */
|
||||||
var preferencesWindowController: PrefsWC?
|
var preferencesWindowController: PreferencesWindowController?
|
||||||
|
|
||||||
/** The window controller of the currently active site list window. */
|
/** The window controller of the currently active site list window. */
|
||||||
var domainListWindowController: DomainListWC?
|
var domainListWindowController: DomainListWindowController?
|
||||||
|
|
||||||
|
/** The window controller of the onboarding window. */
|
||||||
|
var onboardingWindowController: OnboardingWindowController?
|
||||||
|
|
||||||
|
/** The window controller of the warnings window. */
|
||||||
|
var warningsWindowController: WarningsWindowController?
|
||||||
|
|
||||||
/** List of detected (installed) applications that PHP Monitor can work with. */
|
/** List of detected (installed) applications that PHP Monitor can work with. */
|
||||||
var detectedApplications: [Application] = []
|
var detectedApplications: [Application] = []
|
||||||
|
|
||||||
|
/** The services manager, responsible for figuring out what services are active/inactive. */
|
||||||
|
var services = ServicesManager.shared
|
||||||
|
|
||||||
|
/** The warning manager, responsible for keeping track of warnings. */
|
||||||
|
var warnings = WarningManager.shared
|
||||||
|
|
||||||
/** Timer that will periodically reload info about the user's PHP installation. */
|
/** Timer that will periodically reload info about the user's PHP installation. */
|
||||||
var timer: Timer?
|
var timer: Timer?
|
||||||
|
|
||||||
|
@@ -65,7 +65,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
override init() {
|
override init() {
|
||||||
logger.verbosity = .info
|
logger.verbosity = .info
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
logger.verbosity = .performance
|
// logger.verbosity = .performance
|
||||||
#endif
|
#endif
|
||||||
if CommandLine.arguments.contains("--v") {
|
if CommandLine.arguments.contains("--v") {
|
||||||
logger.verbosity = .performance
|
logger.verbosity = .performance
|
||||||
|
@@ -161,14 +161,14 @@ class AppUpdateChecker {
|
|||||||
private static func notifyAboutConnectionIssue() {
|
private static func notifyAboutConnectionIssue() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
BetterAlert().withInformation(
|
BetterAlert().withInformation(
|
||||||
title: "updater.errors.cannot_check_for_update.title".localized,
|
title: "updater.alerts.cannot_check_for_update.title".localized,
|
||||||
subtitle: "updater.errors.cannot_check_for_update.subtitle".localized,
|
subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
|
||||||
description: "updater.errors.cannot_check_for_update.description".localized(
|
description: "updater.alerts.cannot_check_for_update.description".localized(
|
||||||
App.version
|
App.version
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.withTertiary(
|
.withTertiary(
|
||||||
text: "updater.errors.buttons.releases_on_github".localized,
|
text: "updater.alerts.buttons.releases_on_github".localized,
|
||||||
action: { _ in
|
action: { _ in
|
||||||
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
|
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
<capability name="Image references" minToolsVersion="12.0"/>
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
@@ -324,36 +323,32 @@
|
|||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="PQa-AT-b2a">
|
<scene sceneID="PQa-AT-b2a">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" customClass="PrefsWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" customClass="PreferencesWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="h4c-3b-nko">
|
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="h4c-3b-nko">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="372" y="403" width="480" height="270"/>
|
<rect key="contentRect" x="372" y="403" width="550" height="270"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2304" height="1271"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2304" height="1271"/>
|
||||||
<view key="contentView" id="2yL-50-11x">
|
<view key="contentView" id="2yL-50-11x">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
<rect key="frame" x="0.0" y="0.0" width="550" height="270"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="611E3485-DC7F-46A0-8528-11CF9366370C" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconAndLabel" sizeMode="regular" id="fcq-wR-7iv">
|
|
||||||
<allowedToolbarItems/>
|
|
||||||
<defaultToolbarItems/>
|
|
||||||
</toolbar>
|
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="delegate" destination="hLJ-Fd-wRr" id="6HE-8Y-aCO"/>
|
<outlet property="delegate" destination="hLJ-Fd-wRr" id="6HE-8Y-aCO"/>
|
||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="AW2-rV-rbS" kind="relationship" relationship="window.shadowedContentViewController" id="3dX-9V-eA0"/>
|
<segue destination="PCI-2c-55Y" kind="relationship" relationship="window.shadowedContentViewController" id="egC-A4-am8"/>
|
||||||
</connections>
|
</connections>
|
||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-374" y="238"/>
|
<point key="canvasLocation" x="-374" y="238"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Preferences-->
|
<!--Preferences-->
|
||||||
<scene sceneID="iyi-IS-7Ps">
|
<scene sceneID="iyi-IS-7Ps">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController title="Preferences" storyboardIdentifier="preferences" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="PrefsVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="Preferences" identifier="preferencesTemplateVC" storyboardIdentifier="preferencesTemplateVC" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="GenericPreferenceVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" wantsLayer="YES" id="Pf1-A5-3Xz">
|
<view key="view" wantsLayer="YES" id="Pf1-A5-3Xz">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="550" height="498"/>
|
<rect key="frame" x="0.0" y="0.0" width="550" height="498"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
@@ -378,12 +373,32 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="260" y="217"/>
|
<point key="canvasLocation" x="844" y="-153"/>
|
||||||
|
</scene>
|
||||||
|
<!--Tab View Controller-->
|
||||||
|
<scene sceneID="B5x-d3-c7D">
|
||||||
|
<objects>
|
||||||
|
<customObject id="pNW-tM-SQu" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
<tabViewController tabStyle="toolbar" canPropagateSelectedChildViewControllerTitle="NO" id="PCI-2c-55Y" sceneMemberID="viewController">
|
||||||
|
<tabView key="tabView" type="noTabsNoBorder" id="l0U-9a-nM6">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="508" height="300"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<font key="font" metaFont="message"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="PCI-2c-55Y" id="6gR-GR-cwq"/>
|
||||||
|
</connections>
|
||||||
|
</tabView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="tabView" destination="l0U-9a-nM6" id="tfn-UN-1Aa"/>
|
||||||
|
</connections>
|
||||||
|
</tabViewController>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="283" y="-252"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="4XS-kY-YIS">
|
<scene sceneID="4XS-kY-YIS">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController storyboardIdentifier="domainListWindow" id="8Ec-9q-82s" customClass="DomainListWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController storyboardIdentifier="domainListWindow" id="8Ec-9q-82s" customClass="DomainListWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" separatorStyle="line" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
<window key="window" separatorStyle="line" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
@@ -532,7 +547,7 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n5T-nn-k3j">
|
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n5T-nn-k3j">
|
||||||
<rect key="frame" x="13" y="13" width="82" height="32"/>
|
<rect key="frame" x="13" y="13" width="81" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="Tertiary" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="mzA-Uu-gyf">
|
<buttonCell key="cell" type="push" title="Tertiary" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="mzA-Uu-gyf">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@@ -651,7 +666,7 @@ Gw
|
|||||||
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</box>
|
</box>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
||||||
<rect key="frame" x="325" y="13" width="142" height="32"/>
|
<rect key="frame" x="326" y="13" width="141" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="[i18n] Create Link" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WwW-Wv-I8s">
|
<buttonCell key="cell" type="push" title="[i18n] Create Link" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WwW-Wv-I8s">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@@ -732,7 +747,7 @@ Gw
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="900-Z2-tID">
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="900-Z2-tID">
|
||||||
<rect key="frame" x="139" y="23" width="180" height="14"/>
|
<rect key="frame" x="140" y="23" width="180" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="That domain name already exists." id="jOt-n6-TQf">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="That domain name already exists." id="jOt-n6-TQf">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -803,7 +818,7 @@ Gw
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="6IL-DW-37w">
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="6IL-DW-37w">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj">
|
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="626" height="281"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="281"/>
|
||||||
@@ -1381,7 +1396,7 @@ Gw
|
|||||||
</constraints>
|
</constraints>
|
||||||
<textFieldCell key="cell" selectable="YES" alignment="left" id="3i9-RG-Ift">
|
<textFieldCell key="cell" selectable="YES" alignment="left" id="3i9-RG-Ift">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<mutableString key="title">[i18n] Links are used to directly serve projects. If you have a Laravel, Symfony, WordPress, etc. folder with code, you'll want to create a link and choose the folder where your code lives.
If you are in need of a proxy, you can proxy e.g. a container to a particular domain name. This can be useful in combination with Docker, for example.</mutableString>
|
<string key="title">[i18n] Links are used to directly serve projects. If you have a Laravel, Symfony, WordPress, etc. folder with code, you'll want to create a link and choose the folder where your code lives.
If you are in need of a proxy, you can proxy e.g. a container to a particular domain name. This can be useful in combination with Docker, for example.</string>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@@ -1399,6 +1414,7 @@ Gw
|
|||||||
<constraint firstAttribute="bottom" secondItem="pYe-Qu-qnK" secondAttribute="bottom" constant="20" id="lPX-ZF-XZN"/>
|
<constraint firstAttribute="bottom" secondItem="pYe-Qu-qnK" secondAttribute="bottom" constant="20" id="lPX-ZF-XZN"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" constant="20" symbolic="YES" id="spl-Bn-xtw"/>
|
<constraint firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" constant="20" symbolic="YES" id="spl-Bn-xtw"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="FhN-AM-SkI" secondAttribute="bottom" constant="20" symbolic="YES" id="t5w-aL-tOa"/>
|
<constraint firstAttribute="bottom" secondItem="FhN-AM-SkI" secondAttribute="bottom" constant="20" symbolic="YES" id="t5w-aL-tOa"/>
|
||||||
|
<constraint firstItem="pYe-Qu-qnK" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="FhN-AM-SkI" secondAttribute="trailing" constant="8" symbolic="YES" id="y7k-sl-xqe"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</visualEffectView>
|
</visualEffectView>
|
||||||
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cNh-Wc-ADk">
|
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cNh-Wc-ADk">
|
||||||
|
45
phpmon/Domain/App/EnvironmentCheck.swift
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// EnvironmentCheck.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/08/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
||||||
|
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
||||||
|
*/
|
||||||
|
struct EnvironmentCheck {
|
||||||
|
let command: () async -> Bool
|
||||||
|
let name: String
|
||||||
|
let titleText: String
|
||||||
|
let subtitleText: String
|
||||||
|
let descriptionText: String
|
||||||
|
let buttonText: String
|
||||||
|
let requiresAppRestart: Bool
|
||||||
|
|
||||||
|
init(
|
||||||
|
command: @escaping () async -> Bool,
|
||||||
|
name: String,
|
||||||
|
titleText: String,
|
||||||
|
subtitleText: String,
|
||||||
|
descriptionText: String = "",
|
||||||
|
buttonText: String = "OK",
|
||||||
|
requiresAppRestart: Bool = false
|
||||||
|
) {
|
||||||
|
self.command = command
|
||||||
|
self.name = name
|
||||||
|
self.titleText = titleText
|
||||||
|
self.subtitleText = subtitleText
|
||||||
|
self.descriptionText = descriptionText
|
||||||
|
self.buttonText = buttonText
|
||||||
|
self.requiresAppRestart = requiresAppRestart
|
||||||
|
}
|
||||||
|
|
||||||
|
public func succeeds() async -> Bool {
|
||||||
|
return await !self.command()
|
||||||
|
}
|
||||||
|
}
|
@@ -26,10 +26,10 @@ class InterApp {
|
|||||||
DomainListVC.show()
|
DomainListVC.show()
|
||||||
}),
|
}),
|
||||||
InterApp.Action(command: "services/stop", action: { _ in
|
InterApp.Action(command: "services/stop", action: { _ in
|
||||||
MainMenu.shared.stopAllServices()
|
MainMenu.shared.stopValetServices()
|
||||||
}),
|
}),
|
||||||
InterApp.Action(command: "services/restart/all", action: { _ in
|
InterApp.Action(command: "services/restart/all", action: { _ in
|
||||||
MainMenu.shared.restartAllServices()
|
MainMenu.shared.restartValetServices()
|
||||||
}),
|
}),
|
||||||
InterApp.Action(command: "services/restart/nginx", action: { _ in
|
InterApp.Action(command: "services/restart/nginx", action: { _ in
|
||||||
MainMenu.shared.restartNginx()
|
MainMenu.shared.restartNginx()
|
||||||
@@ -56,10 +56,14 @@ class InterApp {
|
|||||||
if PhpEnv.shared.availablePhpVersions.contains(version) {
|
if PhpEnv.shared.availablePhpVersions.contains(version) {
|
||||||
MainMenu.shared.switchToPhpVersion(version)
|
MainMenu.shared.switchToPhpVersion(version)
|
||||||
} else {
|
} else {
|
||||||
BetterAlert().withInformation(
|
DispatchQueue.main.async {
|
||||||
title: "Unsupported version",
|
BetterAlert().withInformation(
|
||||||
subtitle: "PHP Monitor can't switch to PHP \(version), as it may not be installed or available."
|
title: "alert.php_switch_unavailable.title".localized,
|
||||||
).withPrimary(text: "OK").show()
|
subtitle: "alert.php_switch_unavailable.subtitle".localized(version)
|
||||||
|
).withPrimary(
|
||||||
|
text: "alert.php_switch_unavailable.ok".localized
|
||||||
|
).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
]}
|
]}
|
||||||
|
80
phpmon/Domain/App/ServicesManager.swift
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// ServicesManager.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 11/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
class ServicesManager: ObservableObject {
|
||||||
|
|
||||||
|
static var shared = ServicesManager()
|
||||||
|
|
||||||
|
@Published var rootServices: [String: HomebrewService] = [:]
|
||||||
|
@Published var userServices: [String: HomebrewService] = [:]
|
||||||
|
|
||||||
|
public static func loadHomebrewServices(completed: (() -> Void)? = nil) {
|
||||||
|
let rootServiceNames = [
|
||||||
|
PhpEnv.phpInstall.formula,
|
||||||
|
"nginx",
|
||||||
|
"dnsmasq"
|
||||||
|
]
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
let data = Shell
|
||||||
|
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
||||||
|
.data(using: .utf8)!
|
||||||
|
|
||||||
|
let services = try! JSONDecoder()
|
||||||
|
.decode([HomebrewService].self, from: data)
|
||||||
|
.filter({ return rootServiceNames.contains($0.name) })
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
ServicesManager.shared.rootServices = Dictionary(
|
||||||
|
uniqueKeysWithValues: services.map { ($0.name, $0) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let userServiceNames = Preferences.custom.services else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
let data = Shell
|
||||||
|
.pipe("\(Paths.brew) services info --all --json", requiresPath: true)
|
||||||
|
.data(using: .utf8)!
|
||||||
|
|
||||||
|
let services = try! JSONDecoder()
|
||||||
|
.decode([HomebrewService].self, from: data)
|
||||||
|
.filter({ return userServiceNames.contains($0.name) })
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
ServicesManager.shared.userServices = Dictionary(
|
||||||
|
uniqueKeysWithValues: services.map { ($0.name, $0) }
|
||||||
|
)
|
||||||
|
completed?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadData() {
|
||||||
|
Self.loadHomebrewServices()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Dummy data for preview purposes.
|
||||||
|
*/
|
||||||
|
func withDummyServices(_ services: [String: Bool]) -> Self {
|
||||||
|
for (service, enabled) in services {
|
||||||
|
let item = HomebrewService.dummy(named: service, enabled: enabled)
|
||||||
|
self.rootServices[service] = item
|
||||||
|
}
|
||||||
|
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -146,13 +146,15 @@ class Startup {
|
|||||||
command: { return !Shell.pipe("cat /private/etc/sudoers.d/brew").contains(Paths.brew) },
|
command: { return !Shell.pipe("cat /private/etc/sudoers.d/brew").contains(Paths.brew) },
|
||||||
name: "`/private/etc/sudoers.d/brew` contains brew",
|
name: "`/private/etc/sudoers.d/brew` contains brew",
|
||||||
titleText: "startup.errors.sudoers_brew.title".localized,
|
titleText: "startup.errors.sudoers_brew.title".localized,
|
||||||
subtitleText: "startup.errors.sudoers_brew.subtitle".localized
|
subtitleText: "startup.errors.sudoers_brew.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.sudoers_brew.desc".localized
|
||||||
),
|
),
|
||||||
EnvironmentCheck(
|
EnvironmentCheck(
|
||||||
command: { return !Shell.pipe("cat /private/etc/sudoers.d/valet").contains(Paths.valet) },
|
command: { return !Shell.pipe("cat /private/etc/sudoers.d/valet").contains(Paths.valet) },
|
||||||
name: "`/private/etc/sudoers.d/valet` contains valet",
|
name: "`/private/etc/sudoers.d/valet` contains valet",
|
||||||
titleText: "startup.errors.sudoers_valet.title".localized,
|
titleText: "startup.errors.sudoers_valet.title".localized,
|
||||||
subtitleText: "startup.errors.sudoers_valet.subtitle".localized
|
subtitleText: "startup.errors.sudoers_valet.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.sudoers_valet.desc".localized
|
||||||
),
|
),
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// Verify if the Homebrew services are running (as root).
|
// Verify if the Homebrew services are running (as root).
|
||||||
@@ -165,6 +167,18 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.services_json_error.desc".localized
|
descriptionText: "startup.errors.services_json_error.desc".localized
|
||||||
),
|
),
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
// Determine that Valet is installed
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
return !Filesystem.directoryExists("~/.config/valet")
|
||||||
|
},
|
||||||
|
name: "`.config/valet` not empty (Valet installed)",
|
||||||
|
titleText: "startup.errors.valet_not_installed.title".localized,
|
||||||
|
subtitleText: "startup.errors.valet_not_installed.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.valet_not_installed.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
// Determine that the Valet configuration JSON file is valid.
|
// Determine that the Valet configuration JSON file is valid.
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
EnvironmentCheck(
|
EnvironmentCheck(
|
||||||
@@ -182,11 +196,51 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.valet_json_invalid.desc".localized
|
descriptionText: "startup.errors.valet_json_invalid.desc".localized
|
||||||
),
|
),
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
// Check for `which` alias issue
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
return App.architecture == "x86_64"
|
||||||
|
&& FileManager.default.fileExists(atPath: "/usr/local/bin/which")
|
||||||
|
&& Shell.pipe("which node", requiresPath: false)
|
||||||
|
.contains("env: node: No such file or directory")
|
||||||
|
},
|
||||||
|
name: "`env: node` issue does not apply",
|
||||||
|
titleText: "startup.errors.which_alias_issue.title".localized,
|
||||||
|
subtitleText: "startup.errors.which_alias_issue.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.which_alias_issue.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// Determine that Valet works correctly (no issues in platform detected)
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
return valet("--version", sudo: false)
|
||||||
|
.contains("Composer detected issues in your platform")
|
||||||
|
},
|
||||||
|
name: "`no global composer issues",
|
||||||
|
titleText: "startup.errors.global_composer_platform_issues.title".localized,
|
||||||
|
subtitleText: "startup.errors.global_composer_platform_issues.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.global_composer_platform_issues.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
// Determine the Valet version and ensure it isn't unknown.
|
// Determine the Valet version and ensure it isn't unknown.
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
EnvironmentCheck(
|
EnvironmentCheck(
|
||||||
command: {
|
command: {
|
||||||
Valet.shared.version = VersionExtractor.from(valet("--version", sudo: false))
|
let output = valet("--version", sudo: false)
|
||||||
|
// Failure condition #1: does not contain Laravel Valet
|
||||||
|
if !output.contains("Laravel Valet") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Failure condition #2: version cannot be parsed
|
||||||
|
let versionString = output
|
||||||
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
.components(separatedBy: "Laravel Valet")[1]
|
||||||
|
.trimmingCharacters(in: .whitespaces)
|
||||||
|
// Extract the version number
|
||||||
|
Valet.shared.version = VersionExtractor.from(output)
|
||||||
|
// Get the actual version
|
||||||
return Valet.shared.version == nil
|
return Valet.shared.version == nil
|
||||||
},
|
},
|
||||||
name: "`valet --version` was loaded",
|
name: "`valet --version` was loaded",
|
||||||
@@ -195,42 +249,4 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
// MARK: - EnvironmentCheck struct
|
|
||||||
|
|
||||||
/**
|
|
||||||
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
|
||||||
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
|
||||||
*/
|
|
||||||
struct EnvironmentCheck {
|
|
||||||
let command: () async -> Bool
|
|
||||||
let name: String
|
|
||||||
let titleText: String
|
|
||||||
let subtitleText: String
|
|
||||||
let descriptionText: String
|
|
||||||
let buttonText: String
|
|
||||||
let requiresAppRestart: Bool
|
|
||||||
|
|
||||||
init(
|
|
||||||
command: @escaping () async -> Bool,
|
|
||||||
name: String,
|
|
||||||
titleText: String,
|
|
||||||
subtitleText: String,
|
|
||||||
descriptionText: String = "",
|
|
||||||
buttonText: String = "OK",
|
|
||||||
requiresAppRestart: Bool = false
|
|
||||||
) {
|
|
||||||
self.command = command
|
|
||||||
self.name = name
|
|
||||||
self.titleText = titleText
|
|
||||||
self.subtitleText = subtitleText
|
|
||||||
self.descriptionText = descriptionText
|
|
||||||
self.buttonText = buttonText
|
|
||||||
self.requiresAppRestart = requiresAppRestart
|
|
||||||
}
|
|
||||||
|
|
||||||
public func succeeds() async -> Bool {
|
|
||||||
return await !self.command()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -55,7 +55,7 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
|
|||||||
let path = pathControl.url!.path
|
let path = pathControl.url!.path
|
||||||
let name = inputDomainName.stringValue
|
let name = inputDomainName.stringValue
|
||||||
|
|
||||||
if !FileManager.default.fileExists(atPath: path) {
|
if !Filesystem.exists(path) {
|
||||||
Alert.confirm(
|
Alert.confirm(
|
||||||
onWindow: view.window!,
|
onWindow: view.window!,
|
||||||
messageText: "domain_list.alert.folder_missing.title".localized,
|
messageText: "domain_list.alert.folder_missing.title".localized,
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
||||||
static let reusableName = "domainListPhpCell"
|
static let reusableName = "domainListPhpCell"
|
||||||
@@ -27,6 +28,10 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
|||||||
|
|
||||||
imageViewPhpVersionOK.toolTip = nil
|
imageViewPhpVersionOK.toolTip = nil
|
||||||
|
|
||||||
|
imageViewPhpVersionOK.contentTintColor = site.composerPhpCompatibleWithLinked
|
||||||
|
? NSColor(named: "IconColorGreen")
|
||||||
|
: NSColor(named: "IconColorRed")
|
||||||
|
|
||||||
if site.isolatedPhpVersion != nil {
|
if site.isolatedPhpVersion != nil {
|
||||||
imageViewPhpVersionOK.isHidden = false
|
imageViewPhpVersionOK.isHidden = false
|
||||||
imageViewPhpVersionOK.image = NSImage(named: "Isolated")
|
imageViewPhpVersionOK.image = NSImage(named: "Isolated")
|
||||||
@@ -35,6 +40,7 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
|||||||
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
||||||
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
|
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
|
||||||
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp)
|
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,56 +53,25 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
|||||||
@IBAction func pressedPhpVersion(_ sender: Any) {
|
@IBAction func pressedPhpVersion(_ sender: Any) {
|
||||||
guard let site = self.site else { return }
|
guard let site = self.site else { return }
|
||||||
|
|
||||||
let alert = NSAlert.init()
|
var validPhpSuggestions: [PhpVersionNumber] {
|
||||||
alert.alertStyle = .informational
|
if site.isolatedPhpVersion != nil {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
var information = ""
|
return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
|
||||||
|
|
||||||
if self.site?.isolatedPhpVersion != nil {
|
|
||||||
information += "alert.composer_php_isolated.desc".localized(
|
|
||||||
self.site!.isolatedPhpVersion!.versionNumber.homebrewVersion,
|
|
||||||
PhpEnv.phpInstall.version.short
|
|
||||||
)
|
|
||||||
information += "\n\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
|
|
||||||
.localized
|
|
||||||
|
|
||||||
alert.messageText = "alert.composer_php_requirement.title"
|
|
||||||
.localized("\(site.name).\(Valet.shared.config.tld)", site.composerPhp)
|
|
||||||
alert.informativeText = information
|
|
||||||
|
|
||||||
alert.addButton(withTitle: "site_link.close".localized)
|
|
||||||
|
|
||||||
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
|
|
||||||
var map: [Int: String] = [:]
|
|
||||||
|
|
||||||
if site.isolatedPhpVersion == nil {
|
|
||||||
// Determine which installed versions would be ideal to switch to,
|
|
||||||
// but make sure to exclude the currently linked version
|
|
||||||
PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
|
|
||||||
version.homebrewVersion != PhpEnv.phpInstall.version.short
|
version.homebrewVersion != PhpEnv.phpInstall.version.short
|
||||||
}).forEach { version in
|
})
|
||||||
alert.addButton(withTitle: "site_link.switch_to_php".localized(version.homebrewVersion))
|
|
||||||
map[mapIndex] = version.homebrewVersion
|
|
||||||
mapIndex += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Site is not isolated, show options to switch global PHP version
|
|
||||||
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!) { response in
|
|
||||||
if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue {
|
|
||||||
if map.keys.contains(response.rawValue) {
|
|
||||||
let version = map[response.rawValue]!
|
|
||||||
Log.info("Pressed button to switch to \(version)")
|
|
||||||
MainMenu.shared.switchToPhpVersion(version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Site is isolated, do not show any options to switch
|
|
||||||
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let button = self.buttonPhpVersion!
|
||||||
|
let popover = NSPopover()
|
||||||
|
|
||||||
|
let view = VersionPopoverView(site: site, validPhpVersions: validPhpSuggestions, parent: popover)
|
||||||
|
|
||||||
|
popover.contentViewController = NSHostingController(rootView: view)
|
||||||
|
popover.behavior = .transient
|
||||||
|
popover.animates = true
|
||||||
|
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .maxY)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -69,7 +69,8 @@ extension DomainListVC {
|
|||||||
.localized(
|
.localized(
|
||||||
"\(selectedSite.name).\(Valet.shared.config.tld)",
|
"\(selectedSite.name).\(Valet.shared.config.tld)",
|
||||||
newState
|
newState
|
||||||
)
|
),
|
||||||
|
preference: .notifyAboutSecureToggle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ extension DomainListVC {
|
|||||||
|
|
||||||
self.performAction(command: command) {
|
self.performAction(command: command) {
|
||||||
self.selectedSite!.determineIsolated()
|
self.selectedSite!.determineIsolated()
|
||||||
|
self.selectedSite!.determineComposerPhpVersion()
|
||||||
|
|
||||||
if self.selectedSite!.isolatedPhpVersion == nil {
|
if self.selectedSite!.isolatedPhpVersion == nil {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
@@ -133,6 +135,7 @@ extension DomainListVC {
|
|||||||
@objc func removeIsolatedSite() {
|
@objc func removeIsolatedSite() {
|
||||||
self.performAction(command: "sudo \(Paths.valet) unisolate --site '\(self.selectedSite!.name)' && exit;") {
|
self.performAction(command: "sudo \(Paths.valet) unisolate --site '\(self.selectedSite!.name)' && exit;") {
|
||||||
self.selectedSite!.isolatedPhpVersion = nil
|
self.selectedSite!.isolatedPhpVersion = nil
|
||||||
|
self.selectedSite!.determineComposerPhpVersion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -64,17 +64,16 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource
|
|||||||
|
|
||||||
let windowController = storyboard.instantiateController(
|
let windowController = storyboard.instantiateController(
|
||||||
withIdentifier: "domainListWindow"
|
withIdentifier: "domainListWindow"
|
||||||
) as! DomainListWC
|
) as! DomainListWindowController
|
||||||
|
|
||||||
windowController.window!.title = "domain_list.title".localized
|
guard let window = windowController.window else { return }
|
||||||
windowController.window!.subtitle = "domain_list.subtitle".localized
|
|
||||||
windowController.window!.delegate = delegate
|
window.title = "domain_list.title".localized
|
||||||
windowController.window!.styleMask = [
|
window.subtitle = "domain_list.subtitle".localized
|
||||||
.titled, .closable, .resizable, .miniaturizable
|
window.delegate = delegate ?? windowController
|
||||||
]
|
window.styleMask = [.titled, .closable, .resizable, .miniaturizable]
|
||||||
windowController.window!.minSize = NSSize(width: 550, height: 200)
|
window.minSize = NSSize(width: 550, height: 200)
|
||||||
windowController.window!.delegate = windowController
|
window.setFrameAutosaveName("domainListWindow")
|
||||||
windowController.window!.setFrameAutosaveName("domainListWindow")
|
|
||||||
|
|
||||||
App.shared.domainListWindowController = windowController
|
App.shared.domainListWindowController = windowController
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// DomainListWC.swift
|
// DomainListWindowController.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 03/12/2021.
|
// Created by Nico Verbruggen on 03/12/2021.
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
|
class DomainListWindowController: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
|
||||||
|
|
||||||
// MARK: - Window Identifier
|
// MARK: - Window Identifier
|
||||||
|
|
||||||
@@ -124,8 +124,6 @@ class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate
|
|||||||
withIdentifier: "addProxyWindow"
|
withIdentifier: "addProxyWindow"
|
||||||
) as! NSWindowController
|
) as! NSWindowController
|
||||||
|
|
||||||
// let viewController = windowController.window!.contentViewController as! AddSiteVC
|
|
||||||
|
|
||||||
self.window?.beginSheet(windowController.window!)
|
self.window?.beginSheet(windowController.window!)
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -11,7 +11,7 @@ import Cocoa
|
|||||||
|
|
||||||
class SelectionVC: NSViewController {
|
class SelectionVC: NSViewController {
|
||||||
|
|
||||||
weak var domainListWC: DomainListWC?
|
weak var domainListWC: DomainListWindowController?
|
||||||
|
|
||||||
@IBOutlet weak var textFieldTitle: NSTextField!
|
@IBOutlet weak var textFieldTitle: NSTextField!
|
||||||
@IBOutlet weak var textFieldDescription: NSTextField!
|
@IBOutlet weak var textFieldDescription: NSTextField!
|
||||||
|
@@ -13,7 +13,7 @@ class ComposerWindow {
|
|||||||
private var menu: MainMenu?
|
private var menu: MainMenu?
|
||||||
private var shouldNotify: Bool! = nil
|
private var shouldNotify: Bool! = nil
|
||||||
private var completion: ((Bool) -> Void)! = nil
|
private var completion: ((Bool) -> Void)! = nil
|
||||||
private var window: ProgressWindowController?
|
private var window: TerminalProgressWindowController?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Updates the global dependencies and runs the completion callback when done.
|
Updates the global dependencies and runs the completion callback when done.
|
||||||
@@ -25,7 +25,9 @@ class ComposerWindow {
|
|||||||
|
|
||||||
Paths.shared.detectBinaryPaths()
|
Paths.shared.detectBinaryPaths()
|
||||||
if Paths.composer == nil {
|
if Paths.composer == nil {
|
||||||
presentMissingAlert()
|
DispatchQueue.main.async {
|
||||||
|
self.presentMissingAlert()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ class ComposerWindow {
|
|||||||
menu?.setBusyImage()
|
menu?.setBusyImage()
|
||||||
menu?.rebuild()
|
menu?.rebuild()
|
||||||
|
|
||||||
window = ProgressWindowController.display(
|
window = TerminalProgressWindowController.display(
|
||||||
title: "alert.composer_progress.title".localized,
|
title: "alert.composer_progress.title".localized,
|
||||||
description: "alert.composer_progress.info".localized
|
description: "alert.composer_progress.info".localized
|
||||||
)
|
)
|
||||||
@@ -83,7 +85,8 @@ class ComposerWindow {
|
|||||||
if shouldNotify {
|
if shouldNotify {
|
||||||
LocalNotification.send(
|
LocalNotification.send(
|
||||||
title: "alert.composer_success.title".localized,
|
title: "alert.composer_success.title".localized,
|
||||||
subtitle: "alert.composer_success.info".localized
|
subtitle: "alert.composer_success.info".localized,
|
||||||
|
preference: .notifyAboutGlobalComposerStatus
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
window = nil
|
window = nil
|
||||||
@@ -115,7 +118,7 @@ class ComposerWindow {
|
|||||||
|
|
||||||
// MARK: Alert
|
// MARK: Alert
|
||||||
|
|
||||||
private func presentMissingAlert() {
|
@MainActor private func presentMissingAlert() {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "alert.composer_missing.title".localized,
|
title: "alert.composer_missing.title".localized,
|
||||||
|
@@ -38,8 +38,6 @@ struct PhpFrameworks {
|
|||||||
"zendframework/zendframework": "Zend",
|
"zendframework/zendframework": "Zend",
|
||||||
"zendframework/zend-mvc": "Zend",
|
"zendframework/zend-mvc": "Zend",
|
||||||
"typo3/cms-core": "Typo3"
|
"typo3/cms-core": "Typo3"
|
||||||
|
|
||||||
// TODO (6.0): Handle these in v6.0
|
|
||||||
// "magento/*": "Magento",
|
// "magento/*": "Magento",
|
||||||
// "concrete5/*": "Concrete5",
|
// "concrete5/*": "Concrete5",
|
||||||
// "contao/*": "Contao",
|
// "contao/*": "Contao",
|
||||||
@@ -73,7 +71,7 @@ struct PhpFrameworks {
|
|||||||
public static func detectFallbackDependency(_ basePath: String) -> String? {
|
public static func detectFallbackDependency(_ basePath: String) -> String? {
|
||||||
for entry in Self.FileMapping {
|
for entry in Self.FileMapping {
|
||||||
let found = entry.value
|
let found = entry.value
|
||||||
.map { path in return Filesystem.fileExists(basePath + path) }
|
.map { path in return Filesystem.exists(basePath + path) }
|
||||||
.contains(true)
|
.contains(true)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
|
@@ -42,6 +42,35 @@ class HomebrewDiagnostics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
It is possible to upgrade PHP, but forget running `valet install`.
|
||||||
|
This results in a scenario where a rogue www.conf file exists.
|
||||||
|
*/
|
||||||
|
public static func checkForPhpFpmPoolConflicts() {
|
||||||
|
Log.info("Checking for PHP-FPM pool conflicts...")
|
||||||
|
|
||||||
|
// We'll need to know what the primary PHP version is
|
||||||
|
let primary = PhpEnv.shared.currentInstall.version.short
|
||||||
|
|
||||||
|
// Versions to be handled
|
||||||
|
let switcher = InternalSwitcher()
|
||||||
|
var versions = switcher.getVersionsToBeHandled(primary)
|
||||||
|
|
||||||
|
versions = versions.filter { version in
|
||||||
|
return switcher.requiresDisablingOfDefaultPhpFpmPool(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
if versions.isEmpty {
|
||||||
|
Log.info("No PHP-FPM pools need to be fixed. All OK.")
|
||||||
|
}
|
||||||
|
|
||||||
|
versions.forEach { version in
|
||||||
|
switcher.disableDefaultPhpFpmPool(version)
|
||||||
|
switcher.stopPhpVersion(version)
|
||||||
|
switcher.startPhpVersion(version, primary: version == primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Check if the alias conflict as documented in `checkForCaskConflict` actually occurred.
|
Check if the alias conflict as documented in `checkForCaskConflict` actually occurred.
|
||||||
*/
|
*/
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// NginxConfiguration.swift
|
// NginxConfigurationFile.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 15/03/2022.
|
// Created by Nico Verbruggen on 15/03/2022.
|
||||||
@@ -8,36 +8,32 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class NginxConfiguration {
|
class NginxConfigurationFile: CreatedFromFile {
|
||||||
|
|
||||||
/** Contents of the Nginx file in question, as a string. */
|
/// Contents of the Nginx file in question, as a string.
|
||||||
var contents: String!
|
var contents: String!
|
||||||
|
|
||||||
/** The name of the domain, usually derived from the name of the file. */
|
/// The name of the domain, usually derived from the name of the file.
|
||||||
var domain: String
|
var domain: String
|
||||||
|
|
||||||
/** The TLD of the domain, usually derived from the name of the file. */
|
/// The TLD of the domain, usually derived from the name of the file.
|
||||||
var tld: String
|
var tld: String
|
||||||
|
|
||||||
static func from(filePath: String) -> NginxConfiguration? {
|
/** Resolves an nginx configuration file (.conf) */
|
||||||
let path = filePath.replacingOccurrences(
|
static func from(filePath: String) -> Self? {
|
||||||
of: "~",
|
let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
|
||||||
with: "/Users/\(Paths.whoami)"
|
|
||||||
)
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let fileContents = try String(contentsOfFile: path)
|
let fileContents = try String(contentsOfFile: path)
|
||||||
return NginxConfiguration.init(
|
|
||||||
path: path,
|
return Self.init(path: path, contents: fileContents)
|
||||||
contents: fileContents
|
|
||||||
)
|
|
||||||
} catch {
|
} catch {
|
||||||
Log.warn("Could not read the nginx configuration file at: `\(filePath)`")
|
Log.warn("Could not read the nginx configuration file at: `\(filePath)`")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(path: String, contents: String) {
|
required init(path: String, contents: String) {
|
||||||
let domain = String(path.split(separator: "/").last!)
|
let domain = String(path.split(separator: "/").last!)
|
||||||
let tld = String(domain.split(separator: ".").last!)
|
let tld = String(domain.split(separator: ".").last!)
|
||||||
|
|
||||||
@@ -46,9 +42,7 @@ class NginxConfiguration {
|
|||||||
self.tld = tld
|
self.tld = tld
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Retrieves what address this domain is proxying. */
|
||||||
Retrieves what address this domain is proxying.
|
|
||||||
*/
|
|
||||||
lazy var proxy: String? = {
|
lazy var proxy: String? = {
|
||||||
let regex = try! NSRegularExpression(
|
let regex = try! NSRegularExpression(
|
||||||
pattern: #"proxy_pass (?<proxy>.*:\d*)(\/*);"#,
|
pattern: #"proxy_pass (?<proxy>.*:\d*)(\/*);"#,
|
||||||
@@ -61,9 +55,7 @@ class NginxConfiguration {
|
|||||||
return contents[Range(match.range(withName: "proxy"), in: contents)!]
|
return contents[Range(match.range(withName: "proxy"), in: contents)!]
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/**
|
/** Retrieves which isolated version is active for this domain (if applicable). */
|
||||||
Retrieves which isolated version is active for this domain (if applicable).
|
|
||||||
*/
|
|
||||||
lazy var isolatedVersion: String? = {
|
lazy var isolatedVersion: String? = {
|
||||||
let regex = try! NSRegularExpression(
|
let regex = try! NSRegularExpression(
|
||||||
// PHP versions have (so far) never needed multiple digits for version numbers
|
// PHP versions have (so far) never needed multiple digits for version numbers
|
@@ -13,8 +13,11 @@ class ValetProxyScanner: ProxyScanner {
|
|||||||
return try! FileManager
|
return try! FileManager
|
||||||
.default
|
.default
|
||||||
.contentsOfDirectory(atPath: directoryPath)
|
.contentsOfDirectory(atPath: directoryPath)
|
||||||
|
.filter {
|
||||||
|
return !$0.starts(with: ".")
|
||||||
|
}
|
||||||
.compactMap {
|
.compactMap {
|
||||||
return NginxConfiguration.from(filePath: "\(directoryPath)/\($0)")
|
return NginxConfigurationFile.from(filePath: "\(directoryPath)/\($0)")
|
||||||
}
|
}
|
||||||
.filter {
|
.filter {
|
||||||
return $0.proxy != nil
|
return $0.proxy != nil
|
||||||
|
@@ -14,7 +14,7 @@ class ValetProxy: DomainListable {
|
|||||||
var target: String
|
var target: String
|
||||||
var secured: Bool = false
|
var secured: Bool = false
|
||||||
|
|
||||||
init(_ configuration: NginxConfiguration) {
|
init(_ configuration: NginxConfigurationFile) {
|
||||||
self.domain = configuration.domain
|
self.domain = configuration.domain
|
||||||
self.tld = configuration.tld
|
self.tld = configuration.tld
|
||||||
self.target = configuration.proxy!
|
self.target = configuration.proxy!
|
||||||
|
@@ -23,22 +23,26 @@ extension ValetSite {
|
|||||||
self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false)
|
self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false)
|
||||||
self.secured = secure
|
self.secured = secure
|
||||||
self.composerPhp = constraint
|
self.composerPhp = constraint
|
||||||
|
self.composerPhpSource = constraint != "" ? .require : .unknown
|
||||||
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
|
||||||
.map { string in
|
|
||||||
return !PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
|
||||||
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
|
||||||
.isEmpty
|
|
||||||
}.contains(true)
|
|
||||||
|
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
self.driverDeterminedByComposer = true
|
self.driverDeterminedByComposer = true
|
||||||
|
|
||||||
if linked {
|
if linked {
|
||||||
self.aliasPath = self.absolutePath
|
self.aliasPath = self.absolutePath
|
||||||
}
|
}
|
||||||
|
|
||||||
if let isolated = isolated {
|
if let isolated = isolated {
|
||||||
self.isolatedPhpVersion = PhpInstallation(isolated)
|
self.isolatedPhpVersion = PhpInstallation(isolated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
||||||
|
.map { string in
|
||||||
|
let origin = self.isolatedPhpVersion?.versionNumber.homebrewVersion ?? PhpEnv.phpInstall.version.long
|
||||||
|
return !PhpVersionNumberCollection.make(from: [origin])
|
||||||
|
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
.isEmpty
|
||||||
|
}.contains(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ class ValetSite: DomainListable {
|
|||||||
/// replacing the user's home folder with ~.
|
/// replacing the user's home folder with ~.
|
||||||
lazy var absolutePathRelative: String = {
|
lazy var absolutePathRelative: String = {
|
||||||
return self.absolutePath
|
return self.absolutePath
|
||||||
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
.replacingOccurrences(of: Paths.homePath, with: "~")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/// The TLD used to locate this site.
|
/// The TLD used to locate this site.
|
||||||
@@ -81,9 +81,9 @@ class ValetSite: DomainListable {
|
|||||||
|
|
||||||
if makeDeterminations {
|
if makeDeterminations {
|
||||||
determineSecured()
|
determineSecured()
|
||||||
|
determineIsolated()
|
||||||
determineComposerPhpVersion()
|
determineComposerPhpVersion()
|
||||||
determineDriver()
|
determineDriver()
|
||||||
determineIsolated()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +133,6 @@ class ValetSite: DomainListable {
|
|||||||
with the currently linked version of PHP (see `composerPhpMatchesSystem`).
|
with the currently linked version of PHP (see `composerPhpMatchesSystem`).
|
||||||
*/
|
*/
|
||||||
public func determineComposerPhpVersion() {
|
public func determineComposerPhpVersion() {
|
||||||
|
|
||||||
self.determineComposerInformation()
|
self.determineComposerInformation()
|
||||||
self.determineValetPhpFileInfo()
|
self.determineValetPhpFileInfo()
|
||||||
|
|
||||||
@@ -145,7 +144,8 @@ class ValetSite: DomainListable {
|
|||||||
// For example, for Laravel 8 projects the value is "^7.3|^8.0"
|
// For example, for Laravel 8 projects the value is "^7.3|^8.0"
|
||||||
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
||||||
.map { string in
|
.map { string in
|
||||||
return !PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
let origin = self.isolatedPhpVersion?.versionNumber.homebrewVersion ?? PhpEnv.phpInstall.version.long
|
||||||
|
return !PhpVersionNumberCollection.make(from: [origin])
|
||||||
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
.isEmpty
|
.isEmpty
|
||||||
}.contains(true)
|
}.contains(true)
|
||||||
@@ -225,7 +225,7 @@ class ValetSite: DomainListable {
|
|||||||
|
|
||||||
public static func isolatedVersion(_ filePath: String) -> String? {
|
public static func isolatedVersion(_ filePath: String) -> String? {
|
||||||
if Filesystem.fileExists(filePath) {
|
if Filesystem.fileExists(filePath) {
|
||||||
return NginxConfiguration
|
return NginxConfigurationFile
|
||||||
.from(filePath: filePath)?
|
.from(filePath: filePath)?
|
||||||
.isolatedVersion ?? nil
|
.isolatedVersion ?? nil
|
||||||
}
|
}
|
||||||
|
@@ -113,20 +113,11 @@ class Valet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Starts the preload of sites, but only if the maximum amount of sites is 30.
|
Starts the preload of sites. In order to make sure PHP Monitor can correctly
|
||||||
For users with more sites, the site list is loaded when they bring up the site list window.
|
handle all PHP versions including isolation, it needs to know about all sites.
|
||||||
(This is done to keep the startup speed as fast as possible.)
|
|
||||||
*/
|
*/
|
||||||
public func startPreloadingSites() {
|
public func startPreloadingSites() {
|
||||||
let maximumPreload = 50
|
self.reloadSites()
|
||||||
let foundSites = self.countPaths()
|
|
||||||
if foundSites <= maximumPreload {
|
|
||||||
// Preload the sites and their drivers
|
|
||||||
Log.info("Fewer than or \(maximumPreload) sites found, preloading list of sites...")
|
|
||||||
self.reloadSites()
|
|
||||||
} else {
|
|
||||||
Log.info("\(foundSites) sites found, exceeds \(maximumPreload) for preload at launch!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -192,6 +183,11 @@ class Valet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func hasPlatformIssues() -> Bool {
|
||||||
|
return valet("--version", sudo: false)
|
||||||
|
.contains("Composer detected issues in your platform")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns a count of how many sites are linked and parked.
|
Returns a count of how many sites are linked and parked.
|
||||||
*/
|
*/
|
||||||
@@ -225,6 +221,8 @@ class Valet {
|
|||||||
sites.insert(site, at: 0)
|
sites.insert(site, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.info("\(sites.count) sites & \(proxies.count) proxies have been scanned.")
|
||||||
|
|
||||||
isBusy = false
|
isBusy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// HeaderView.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 04/02/2021.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class HeaderView: NSView, XibLoadable {
|
|
||||||
|
|
||||||
@IBOutlet weak var textField: NSTextField!
|
|
||||||
|
|
||||||
static func asMenuItem(text: String) -> NSMenuItem {
|
|
||||||
let view = Self.createFromXib()
|
|
||||||
view!.textField.stringValue = text.uppercased()
|
|
||||||
let item = NSMenuItem()
|
|
||||||
item.view = view
|
|
||||||
item.target = self
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner"/>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<customView id="c22-O7-iKe" customClass="HeaderView" customModule="PHP_Monitor" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="270" height="24"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ddg-VQ-cOT">
|
|
||||||
<rect key="frame" x="12" y="5" width="113" height="15"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="ACTIVE SERVICES" id="NHz-MZ-8FK">
|
|
||||||
<font key="font" metaFont="systemBold" size="12"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="ddg-VQ-cOT" firstAttribute="centerY" secondItem="c22-O7-iKe" secondAttribute="centerY" id="n4Z-WN-RIh"/>
|
|
||||||
<constraint firstItem="ddg-VQ-cOT" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="14" id="yuW-pb-GQJ"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<outlet property="textField" destination="ddg-VQ-cOT" id="aaQ-Xb-o2X"/>
|
|
||||||
</connections>
|
|
||||||
<point key="canvasLocation" x="177" y="105"/>
|
|
||||||
</customView>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
291
phpmon/Domain/Menu/MainMenu+Actions.swift
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+Actions.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 19/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension MainMenu {
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
@objc func fixHomebrewPermissions() {
|
||||||
|
if !BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.fix_homebrew_permissions.title".localized,
|
||||||
|
subtitle: "alert.fix_homebrew_permissions.subtitle".localized,
|
||||||
|
description: "alert.fix_homebrew_permissions.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.fix_homebrew_permissions.ok".localized)
|
||||||
|
.withSecondary(text: "alert.fix_homebrew_permissions.cancel".localized)
|
||||||
|
.didSelectPrimary() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
asyncExecution {
|
||||||
|
try Actions.fixHomebrewPermissions()
|
||||||
|
} success: {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.fix_homebrew_permissions_done.title".localized,
|
||||||
|
subtitle: "alert.fix_homebrew_permissions_done.subtitle".localized,
|
||||||
|
description: "alert.fix_homebrew_permissions_done.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
} failure: { error in
|
||||||
|
BetterAlert.show(for: error as! HomebrewPermissionError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func restartPhpFpm() {
|
||||||
|
asyncExecution {
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func restartValetServices() {
|
||||||
|
asyncExecution {
|
||||||
|
Actions.restartDnsMasq()
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
Actions.restartNginx()
|
||||||
|
} success: {
|
||||||
|
LocalNotification.send(
|
||||||
|
title: "notification.services_restarted".localized,
|
||||||
|
subtitle: "notification.services_restarted_desc".localized,
|
||||||
|
preference: .notifyAboutServices
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func stopValetServices() {
|
||||||
|
asyncExecution {
|
||||||
|
Actions.stopValetServices()
|
||||||
|
} success: {
|
||||||
|
LocalNotification.send(
|
||||||
|
title: "notification.services_stopped".localized,
|
||||||
|
subtitle: "notification.services_stopped_desc".localized,
|
||||||
|
preference: .notifyAboutServices
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func restartNginx() {
|
||||||
|
asyncExecution {
|
||||||
|
Actions.restartNginx()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func restartDnsMasq() {
|
||||||
|
asyncExecution {
|
||||||
|
Actions.restartDnsMasq()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func disableAllXdebugModes() {
|
||||||
|
guard let file = PhpEnv.shared.getConfigFile(forKey: "xdebug.mode") else {
|
||||||
|
Log.info("xdebug.mode could not be found in any .ini file, aborting.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try file.replace(key: "xdebug.mode", value: "off")
|
||||||
|
|
||||||
|
Log.perf("Refreshing menu...")
|
||||||
|
MainMenu.shared.rebuild()
|
||||||
|
restartPhpFpm()
|
||||||
|
} catch {
|
||||||
|
Log.err("There was an issue replacing `xdebug.mode` in \(file.filePath)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func toggleXdebugMode(sender: XdebugMenuItem) {
|
||||||
|
Log.info("Switching Xdebug to mode: \(sender.mode)")
|
||||||
|
|
||||||
|
guard let file = PhpEnv.shared.getConfigFile(forKey: "xdebug.mode") else {
|
||||||
|
return Log.info("xdebug.mode could not be found in any .ini file, aborting.")
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
var modes = Xdebug.activeModes
|
||||||
|
|
||||||
|
if let index = modes.firstIndex(of: sender.mode) {
|
||||||
|
modes.remove(at: index)
|
||||||
|
} else {
|
||||||
|
modes.append(sender.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var newValue = modes.joined(separator: ",")
|
||||||
|
if newValue.isEmpty {
|
||||||
|
newValue = "off"
|
||||||
|
}
|
||||||
|
|
||||||
|
try file.replace(key: "xdebug.mode", value: newValue)
|
||||||
|
|
||||||
|
Log.perf("Refreshing menu...")
|
||||||
|
MainMenu.shared.rebuild()
|
||||||
|
restartPhpFpm()
|
||||||
|
} catch {
|
||||||
|
Log.err("There was an issue replacing `xdebug.mode` in \(file.filePath)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
||||||
|
asyncExecution {
|
||||||
|
sender.phpExtension?.toggle()
|
||||||
|
|
||||||
|
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func performRollback() {
|
||||||
|
asyncExecution {
|
||||||
|
PresetHelper.rollbackPreset?.apply()
|
||||||
|
PresetHelper.rollbackPreset = nil
|
||||||
|
MainMenu.shared.rebuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor @objc func rollbackPreset() {
|
||||||
|
guard let preset = PresetHelper.rollbackPreset else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "alert.revert_description.title".localized,
|
||||||
|
subtitle: "alert.revert_description.subtitle".localized(
|
||||||
|
preset.textDescription
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.revert_description.ok".localized, action: { alert in
|
||||||
|
alert.close(with: .OK)
|
||||||
|
self.performRollback()
|
||||||
|
})
|
||||||
|
.withSecondary(text: "alert.revert_description.cancel".localized)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func togglePreset(sender: PresetMenuItem) {
|
||||||
|
asyncExecution {
|
||||||
|
sender.preset?.apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor @objc func showPresetHelp() {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "preset_help_title".localized,
|
||||||
|
subtitle: "preset_help_info".localized,
|
||||||
|
description: "preset_help_desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.withTertiary(text: "", action: { alert in
|
||||||
|
NSWorkspace.shared.open(Constants.Urls.FrequentlyAskedQuestions)
|
||||||
|
alert.close(with: .OK)
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openPhpInfo() {
|
||||||
|
var url: URL?
|
||||||
|
|
||||||
|
asyncWithBusyUI {
|
||||||
|
url = Actions.createTempPhpInfoFile()
|
||||||
|
} completion: {
|
||||||
|
if url != nil { NSWorkspace.shared.open(url!) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateGlobalComposerDependencies() {
|
||||||
|
ComposerWindow().updateGlobalDependencies(
|
||||||
|
notify: true,
|
||||||
|
completion: { _ in }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openActiveConfigFolder() {
|
||||||
|
if PhpEnv.phpInstall.version.error {
|
||||||
|
Actions.openGenericPhpConfigFolder()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions.openPhpConfigFolder(version: PhpEnv.phpInstall.version.short)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openPhpMonitorConfigurationFile() {
|
||||||
|
Actions.openPhpMonitorConfigFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openGlobalComposerFolder() {
|
||||||
|
Actions.openGlobalComposerFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openValetConfigFolder() {
|
||||||
|
Actions.openValetConfigFolder()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func switchToPhpVersion(sender: PhpMenuItem) {
|
||||||
|
self.switchToPhpVersion(sender.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func switchToPhpVersion(_ version: String) {
|
||||||
|
setBusyImage()
|
||||||
|
PhpEnv.shared.isBusy = true
|
||||||
|
PhpEnv.shared.delegate = self
|
||||||
|
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
|
updatePhpVersionInStatusBar()
|
||||||
|
rebuild()
|
||||||
|
PhpEnv.switcher.performSwitch(
|
||||||
|
to: version,
|
||||||
|
completion: {
|
||||||
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
|
App.shared.handlePhpConfigWatcher()
|
||||||
|
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Async
|
||||||
|
|
||||||
|
/**
|
||||||
|
This async-friendly version of the switcher can be invoked elsewhere in the app:
|
||||||
|
```
|
||||||
|
Task {
|
||||||
|
await MainMenu.shared.switchToPhp("8.1")
|
||||||
|
// thing to do after the switch
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Since this async function uses `withCheckedContinuation`
|
||||||
|
any code after will run only after the switcher is done.
|
||||||
|
*/
|
||||||
|
func switchToPhp(_ version: String) async {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
setBusyImage()
|
||||||
|
PhpEnv.shared.isBusy = true
|
||||||
|
PhpEnv.shared.delegate = self
|
||||||
|
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await withCheckedContinuation({ continuation in
|
||||||
|
updatePhpVersionInStatusBar()
|
||||||
|
rebuild()
|
||||||
|
PhpEnv.switcher.performSwitch(
|
||||||
|
to: version,
|
||||||
|
completion: {
|
||||||
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
|
App.shared.handlePhpConfigWatcher()
|
||||||
|
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
|
continuation.resume()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -36,8 +36,8 @@ extension MainMenu {
|
|||||||
*/
|
*/
|
||||||
func asyncExecution(
|
func asyncExecution(
|
||||||
_ execute: @escaping () throws -> Void,
|
_ execute: @escaping () throws -> Void,
|
||||||
success: @escaping () -> Void = {},
|
success: @MainActor @escaping () -> Void = {},
|
||||||
failure: @escaping (Error) -> Void = { _ in },
|
failure: @MainActor @escaping (Error) -> Void = { _ in },
|
||||||
behaviours: [AsyncBehaviour] = [
|
behaviours: [AsyncBehaviour] = [
|
||||||
.setsBusyUI,
|
.setsBusyUI,
|
||||||
.reloadsPhpInstallation,
|
.reloadsPhpInstallation,
|
||||||
@@ -74,7 +74,7 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if behaviours.contains(.broadcastServicesUpdate) {
|
if behaviours.contains(.broadcastServicesUpdate) {
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
ServicesManager.shared.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
error == nil ? success() : failure(error!)
|
error == nil ? success() : failure(error!)
|
||||||
|
@@ -11,7 +11,7 @@ import AppKit
|
|||||||
|
|
||||||
extension MainMenu {
|
extension MainMenu {
|
||||||
|
|
||||||
@objc func fixMyValet() {
|
@MainActor @objc func fixMyValet() {
|
||||||
let previousVersion = PhpEnv.phpInstall.version.short
|
let previousVersion = PhpEnv.phpInstall.version.short
|
||||||
|
|
||||||
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
||||||
@@ -42,7 +42,7 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentAlertForMissingFormula() {
|
@MainActor private func presentAlertForMissingFormula() {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "alert.php_formula_missing.title".localized,
|
title: "alert.php_formula_missing.title".localized,
|
||||||
@@ -52,7 +52,7 @@ extension MainMenu {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentAlertForSameVersion() {
|
@MainActor private func presentAlertForSameVersion() {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "alert.fix_my_valet_done.title".localized,
|
title: "alert.fix_my_valet_done.title".localized,
|
||||||
@@ -63,7 +63,7 @@ extension MainMenu {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentAlertForDifferentVersion(version: String) {
|
@MainActor private func presentAlertForDifferentVersion(version: String) {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "alert.fix_my_valet_done.title".localized,
|
title: "alert.fix_my_valet_done.title".localized,
|
||||||
|
@@ -49,35 +49,26 @@ extension MainMenu {
|
|||||||
// Check for an alias conflict
|
// Check for an alias conflict
|
||||||
HomebrewDiagnostics.checkForCaskConflict()
|
HomebrewDiagnostics.checkForCaskConflict()
|
||||||
|
|
||||||
|
// Update the icon
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
|
|
||||||
Log.info("Determining broken PHP-FPM...")
|
|
||||||
// Attempt to find out if PHP-FPM is broken
|
// Attempt to find out if PHP-FPM is broken
|
||||||
|
Log.info("Determining broken PHP-FPM...")
|
||||||
let installation = PhpEnv.phpInstall
|
let installation = PhpEnv.phpInstall
|
||||||
installation.notifyAboutBrokenPhpFpm()
|
installation.notifyAboutBrokenPhpFpm()
|
||||||
|
|
||||||
// Set up the config watchers on launch
|
// Check for other problems
|
||||||
// (these are automatically updated via delegate methods if the user switches)
|
WarningManager.shared.evaluateWarnings()
|
||||||
|
|
||||||
|
// Set up the config watchers on launch (updated automatically when switching)
|
||||||
Log.info("Setting up watchers...")
|
Log.info("Setting up watchers...")
|
||||||
App.shared.handlePhpConfigWatcher()
|
App.shared.handlePhpConfigWatcher()
|
||||||
|
|
||||||
// Detect applications (preset + custom)
|
// Detect built-in and custom applications
|
||||||
Log.info("Detecting applications...")
|
detectApplications()
|
||||||
App.shared.detectedApplications = Application.detectPresetApplications()
|
|
||||||
|
|
||||||
let customApps = Preferences.custom.scanApps.map { appName in
|
// Load the rollback preset
|
||||||
return Application(appName, .user_supplied)
|
PresetHelper.loadRollbackPresetFromFile()
|
||||||
}.filter { app in
|
|
||||||
return app.isInstalled()
|
|
||||||
}
|
|
||||||
|
|
||||||
App.shared.detectedApplications.append(contentsOf: customApps)
|
|
||||||
|
|
||||||
let appNames = App.shared.detectedApplications.map { app in
|
|
||||||
return app.name
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.info("Detected applications: \(appNames)")
|
|
||||||
|
|
||||||
// Load the global hotkey
|
// Load the global hotkey
|
||||||
App.shared.loadGlobalHotkey()
|
App.shared.loadGlobalHotkey()
|
||||||
@@ -85,29 +76,36 @@ extension MainMenu {
|
|||||||
// Preload sites
|
// Preload sites
|
||||||
Valet.shared.startPreloadingSites()
|
Valet.shared.startPreloadingSites()
|
||||||
|
|
||||||
|
// After preloading sites, check for PHP-FPM pool conflicts
|
||||||
|
HomebrewDiagnostics.checkForPhpFpmPoolConflicts()
|
||||||
|
|
||||||
// A non-default TLD is not officially supported since Valet 3.2.x
|
// A non-default TLD is not officially supported since Valet 3.2.x
|
||||||
Valet.notifyAboutUnsupportedTLD()
|
Valet.notifyAboutUnsupportedTLD()
|
||||||
|
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
// Find out which services are active
|
||||||
|
ServicesManager.shared.loadData()
|
||||||
|
|
||||||
// Schedule a request to fetch the PHP version every 60 seconds
|
// Start the background refresh timer
|
||||||
DispatchQueue.main.async { [self] in
|
startSharedTimer()
|
||||||
App.shared.timer = Timer.scheduledTimer(
|
|
||||||
timeInterval: 60,
|
|
||||||
target: self,
|
|
||||||
selector: #selector(refreshActiveInstallation),
|
|
||||||
userInfo: nil,
|
|
||||||
repeats: true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Update the stats
|
||||||
Stats.incrementSuccessfulLaunchCount()
|
Stats.incrementSuccessfulLaunchCount()
|
||||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||||
|
|
||||||
|
// Present first launch screen if needed
|
||||||
|
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
|
||||||
|
Log.info("Should present the first launch screen!")
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
OnboardingWindowController.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for updates
|
||||||
DispatchQueue.global(qos: .utility).async {
|
DispatchQueue.global(qos: .utility).async {
|
||||||
AppUpdateChecker.checkIfNewerVersionIsAvailable()
|
AppUpdateChecker.checkIfNewerVersionIsAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are ready!
|
||||||
Log.info("PHP Monitor is ready to serve!")
|
Log.info("PHP Monitor is ready to serve!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,4 +131,42 @@ extension MainMenu {
|
|||||||
Task { await startup() }
|
Task { await startup() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Schedule a request to fetch the PHP version every 60 seconds.
|
||||||
|
*/
|
||||||
|
private func startSharedTimer() {
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
App.shared.timer = Timer.scheduledTimer(
|
||||||
|
timeInterval: 60,
|
||||||
|
target: self,
|
||||||
|
selector: #selector(refreshActiveInstallation),
|
||||||
|
userInfo: nil,
|
||||||
|
repeats: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Detect which applications are installed that can be used to open a domain's source directory.
|
||||||
|
*/
|
||||||
|
private func detectApplications() {
|
||||||
|
Log.info("Detecting applications...")
|
||||||
|
|
||||||
|
App.shared.detectedApplications = Application.detectPresetApplications()
|
||||||
|
|
||||||
|
let customApps = Preferences.custom.scanApps.map { appName in
|
||||||
|
return Application(appName, .user_supplied)
|
||||||
|
}.filter { app in
|
||||||
|
return app.isInstalled()
|
||||||
|
}
|
||||||
|
|
||||||
|
App.shared.detectedApplications.append(contentsOf: customApps)
|
||||||
|
|
||||||
|
let appNames = App.shared.detectedApplications.map { app in
|
||||||
|
return app.name
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("Detected applications: \(appNames)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,12 +15,6 @@ extension MainMenu {
|
|||||||
func switcherDidStartSwitching(to version: String) {}
|
func switcherDidStartSwitching(to version: String) {}
|
||||||
|
|
||||||
func switcherDidCompleteSwitch(to version: String) {
|
func switcherDidCompleteSwitch(to version: String) {
|
||||||
// Update the PHP version
|
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
|
||||||
|
|
||||||
// Ensure the config watcher gets reloaded
|
|
||||||
App.shared.handlePhpConfigWatcher()
|
|
||||||
|
|
||||||
// Mark as no longer busy
|
// Mark as no longer busy
|
||||||
PhpEnv.shared.isBusy = false
|
PhpEnv.shared.isBusy = false
|
||||||
|
|
||||||
@@ -45,22 +39,32 @@ extension MainMenu {
|
|||||||
self.notifyAboutVersionChange(to: version)
|
self.notifyAboutVersionChange(to: version)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.notifyAboutVersionChange(to: version)
|
self.notifyAboutVersionChange(to: version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if Valet still works correctly
|
||||||
|
self.checkForPlatformIssues()
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
Stats.incrementSuccessfulSwitchCount()
|
Stats.incrementSuccessfulSwitchCount()
|
||||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor private func checkForPlatformIssues() {
|
||||||
|
if Valet.shared.hasPlatformIssues() {
|
||||||
|
Log.info("Composer platform issue(s) detected.")
|
||||||
|
self.suggestFixMyComposer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor private func suggestFixMyValet(failed version: String) {
|
@MainActor private func suggestFixMyValet(failed version: String) {
|
||||||
let outcome = BetterAlert()
|
let outcome = BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "alert.php_switch_failed.title".localized(version),
|
title: "alert.php_switch_failed.title".localized(version),
|
||||||
subtitle: "alert.php_switch_failed.info".localized(version)
|
subtitle: "alert.php_switch_failed.info".localized(version),
|
||||||
|
description: "alert.php_switch_failed.desc".localized()
|
||||||
)
|
)
|
||||||
.withPrimary(text: "alert.php_switch_failed.confirm".localized)
|
.withPrimary(text: "alert.php_switch_failed.confirm".localized)
|
||||||
.withSecondary(text: "alert.php_switch_failed.cancel".localized)
|
.withSecondary(text: "alert.php_switch_failed.cancel".localized)
|
||||||
@@ -70,6 +74,32 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor private func suggestFixMyComposer() {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "alert.global_composer_platform_issues.title".localized,
|
||||||
|
subtitle: "alert.global_composer_platform_issues.subtitle".localized,
|
||||||
|
description: "alert.global_composer_platform_issues.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.global_composer_platform_issues.buttons.update".localized, action: { alert in
|
||||||
|
alert.close(with: .OK)
|
||||||
|
Log.info("The user has chosen to update global dependencies.")
|
||||||
|
ComposerWindow().updateGlobalDependencies(
|
||||||
|
notify: true,
|
||||||
|
completion: { success in
|
||||||
|
Log.info("Dependencies updated successfully: \(success)")
|
||||||
|
Log.info("Re-checking for platform issue(s)...")
|
||||||
|
self.checkForPlatformIssues()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.withSecondary(text: "", action: nil)
|
||||||
|
.withTertiary(text: "alert.global_composer_platform_issues.buttons.quit".localized, action: { alert in
|
||||||
|
alert.close(with: .OK)
|
||||||
|
self.terminateApp()
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
private func reloadDomainListData() {
|
private func reloadDomainListData() {
|
||||||
if let window = App.shared.domainListWindowController {
|
if let window = App.shared.domainListWindowController {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@@ -81,11 +111,14 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func notifyAboutVersionChange(to version: String) {
|
private func notifyAboutVersionChange(to version: String) {
|
||||||
LocalNotification.send(
|
DispatchQueue.main.async {
|
||||||
title: String(format: "notification.version_changed_title".localized, version),
|
LocalNotification.send(
|
||||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
title: String(format: "notification.version_changed_title".localized, version),
|
||||||
)
|
subtitle: String(format: "notification.version_changed_desc".localized, version),
|
||||||
|
preference: .notifyAboutVersionChange
|
||||||
|
)
|
||||||
|
|
||||||
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,12 +11,17 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
|
|
||||||
static let shared = MainMenu()
|
static let shared = MainMenu()
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
statusItem.isVisible = !isRunningSwiftUIPreview
|
||||||
|
}
|
||||||
|
|
||||||
weak var menuDelegate: NSMenuDelegate?
|
weak var menuDelegate: NSMenuDelegate?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The status bar item with variable length.
|
The status bar item with variable length.
|
||||||
*/
|
*/
|
||||||
let statusItem = NSStatusBar.system.statusItem(
|
@MainActor let statusItem = NSStatusBar.system.statusItem(
|
||||||
withLength: NSStatusItem.variableLength
|
withLength: NSStatusItem.variableLength
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -45,29 +50,22 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
Use `rebuild(async:)` to ensure the rebuilding happens in the background.
|
Use `rebuild(async:)` to ensure the rebuilding happens in the background.
|
||||||
*/
|
*/
|
||||||
private func rebuildMenu() {
|
private func rebuildMenu() {
|
||||||
// Create a new menu
|
|
||||||
let menu = StatusMenu()
|
let menu = StatusMenu()
|
||||||
|
|
||||||
// Add the PHP versions (or error messages)
|
|
||||||
menu.addPhpVersionMenuItems()
|
menu.addPhpVersionMenuItems()
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
// Add the possible actions
|
|
||||||
menu.addPhpActionMenuItems()
|
menu.addPhpActionMenuItems()
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
// Add Valet interactions
|
|
||||||
menu.addValetMenuItems()
|
menu.addValetMenuItems()
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
// Add services
|
|
||||||
menu.addRemainingMenuItems()
|
menu.addRemainingMenuItems()
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
// Add about & quit menu items
|
|
||||||
menu.addCoreMenuItems()
|
menu.addCoreMenuItems()
|
||||||
|
|
||||||
// Make sure every item can be interacted with
|
|
||||||
menu.items.forEach({ (item) in
|
menu.items.forEach({ (item) in
|
||||||
item.target = self
|
item.target = self
|
||||||
})
|
})
|
||||||
@@ -79,7 +77,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
/**
|
/**
|
||||||
Sets the status bar image based on a version string.
|
Sets the status bar image based on a version string.
|
||||||
*/
|
*/
|
||||||
func setStatusBarImage(version: String) {
|
@MainActor func setStatusBarImage(version: String) {
|
||||||
setStatusBar(
|
setStatusBar(
|
||||||
image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue)
|
image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue)
|
||||||
? MenuBarImageGenerator.textToImageWithIcon(text: version)
|
? MenuBarImageGenerator.textToImageWithIcon(text: version)
|
||||||
@@ -91,7 +89,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
Sets the status bar image, based on the provided NSImage.
|
Sets the status bar image, based on the provided NSImage.
|
||||||
The image will be used as a template image.
|
The image will be used as a template image.
|
||||||
*/
|
*/
|
||||||
func setStatusBar(image: NSImage) {
|
@MainActor func setStatusBar(image: NSImage) {
|
||||||
if let button = statusItem.button {
|
if let button = statusItem.button {
|
||||||
image.isTemplate = true
|
image.isTemplate = true
|
||||||
button.image = image
|
button.image = image
|
||||||
@@ -124,7 +122,17 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
refreshActiveInstallation()
|
refreshActiveInstallation()
|
||||||
refreshIcon()
|
refreshIcon()
|
||||||
rebuild(async: false)
|
rebuild(async: false)
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
ServicesManager.shared.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows the Welcome Tour screen, again.
|
||||||
|
Did this need a comment? No, probably not.
|
||||||
|
*/
|
||||||
|
@objc func showWelcomeTour() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
OnboardingWindowController.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reloads the menu in the background, using `asyncExecution`. */
|
/** Reloads the menu in the background, using `asyncExecution`. */
|
||||||
@@ -165,155 +173,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
|
||||||
|
|
||||||
@objc func fixHomebrewPermissions() {
|
|
||||||
if !BetterAlert()
|
|
||||||
.withInformation(
|
|
||||||
title: "alert.fix_homebrew_permissions.title".localized,
|
|
||||||
subtitle: "alert.fix_homebrew_permissions.subtitle".localized,
|
|
||||||
description: "alert.fix_homebrew_permissions.desc".localized
|
|
||||||
)
|
|
||||||
.withPrimary(text: "alert.fix_homebrew_permissions.ok".localized)
|
|
||||||
.withSecondary(text: "alert.fix_homebrew_permissions.cancel".localized)
|
|
||||||
.didSelectPrimary() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
asyncExecution {
|
|
||||||
try Actions.fixHomebrewPermissions()
|
|
||||||
} success: {
|
|
||||||
BetterAlert()
|
|
||||||
.withInformation(
|
|
||||||
title: "alert.fix_homebrew_permissions_done.title".localized,
|
|
||||||
subtitle: "alert.fix_homebrew_permissions_done.subtitle".localized,
|
|
||||||
description: "alert.fix_homebrew_permissions_done.desc".localized
|
|
||||||
)
|
|
||||||
.withPrimary(text: "OK")
|
|
||||||
.show()
|
|
||||||
} failure: { error in
|
|
||||||
BetterAlert.show(for: error as! HomebrewPermissionError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func restartPhpFpm() {
|
|
||||||
asyncExecution {
|
|
||||||
Actions.restartPhpFpm()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func restartAllServices() {
|
|
||||||
asyncExecution {
|
|
||||||
Actions.restartDnsMasq()
|
|
||||||
Actions.restartPhpFpm()
|
|
||||||
Actions.restartNginx()
|
|
||||||
} success: {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
LocalNotification.send(
|
|
||||||
title: "notification.services_restarted".localized,
|
|
||||||
subtitle: "notification.services_restarted_desc".localized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func stopAllServices() {
|
|
||||||
asyncExecution {
|
|
||||||
Actions.stopAllServices()
|
|
||||||
} success: {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
LocalNotification.send(
|
|
||||||
title: "notification.services_stopped".localized,
|
|
||||||
subtitle: "notification.services_stopped_desc".localized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func restartNginx() {
|
|
||||||
asyncExecution {
|
|
||||||
Actions.restartNginx()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func restartDnsMasq() {
|
|
||||||
asyncExecution {
|
|
||||||
Actions.restartDnsMasq()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func toggleXdebugMode(sender: XdebugMenuItem) {
|
|
||||||
Log.info("Switching Xdebug to mode: \(sender.mode)")
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
|
||||||
asyncExecution {
|
|
||||||
sender.phpExtension?.toggle()
|
|
||||||
|
|
||||||
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
|
||||||
Actions.restartPhpFpm()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func openPhpInfo() {
|
|
||||||
var url: URL?
|
|
||||||
|
|
||||||
asyncWithBusyUI {
|
|
||||||
url = Actions.createTempPhpInfoFile()
|
|
||||||
} completion: {
|
|
||||||
if url != nil { NSWorkspace.shared.open(url!) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateGlobalComposerDependencies() {
|
|
||||||
ComposerWindow().updateGlobalDependencies(
|
|
||||||
notify: true,
|
|
||||||
completion: { _ in }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func openActiveConfigFolder() {
|
|
||||||
if PhpEnv.phpInstall.version.error {
|
|
||||||
// php version was not identified
|
|
||||||
Actions.openGenericPhpConfigFolder()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// php version was identified
|
|
||||||
Actions.openPhpConfigFolder(version: PhpEnv.phpInstall.version.short)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func openGlobalComposerFolder() {
|
|
||||||
Actions.openGlobalComposerFolder()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func openValetConfigFolder() {
|
|
||||||
Actions.openValetConfigFolder()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func switchToPhpVersion(sender: PhpMenuItem) {
|
|
||||||
self.switchToPhpVersion(sender.version)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func switchToPhpVersion(_ version: String) {
|
|
||||||
setBusyImage()
|
|
||||||
PhpEnv.shared.isBusy = true
|
|
||||||
PhpEnv.shared.delegate = self
|
|
||||||
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
|
||||||
updatePhpVersionInStatusBar()
|
|
||||||
rebuild()
|
|
||||||
PhpEnv.switcher.performSwitch(
|
|
||||||
to: version,
|
|
||||||
completion: {
|
|
||||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Menu Item Functionality
|
// MARK: - Menu Item Functionality
|
||||||
|
|
||||||
@objc func openAbout() {
|
@objc func openAbout() {
|
||||||
@@ -322,7 +181,11 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func openPrefs() {
|
@objc func openPrefs() {
|
||||||
PrefsVC.show()
|
PreferencesWindowController.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func openWarnings() {
|
||||||
|
WarningsWindowController.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openDomainList() {
|
@objc func openDomainList() {
|
||||||
@@ -348,7 +211,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
func menuWillOpen(_ menu: NSMenu) {
|
func menuWillOpen(_ menu: NSMenu) {
|
||||||
// Make sure the shortcut key does not trigger this when the menu is open
|
// Make sure the shortcut key does not trigger this when the menu is open
|
||||||
App.shared.shortcutHotkey?.isPaused = true
|
App.shared.shortcutHotkey?.isPaused = true
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
ServicesManager.shared.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func menuDidClose(_ menu: NSMenu) {
|
func menuDidClose(_ menu: NSMenu) {
|
||||||
|
@@ -1,93 +0,0 @@
|
|||||||
//
|
|
||||||
// StatsView.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 04/02/2021.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
/**
|
|
||||||
The ServicesView is an example of a view that I consider to be "poorly" set up.
|
|
||||||
Why ship it like this, then? Well, it works — that's reason number one, really.
|
|
||||||
|
|
||||||
However, I do believe this should be refactored at some point. Here's why:
|
|
||||||
this view is responsible for retaining the information about the services status.
|
|
||||||
|
|
||||||
The status of the services should live somewhere else, and the fetching of said
|
|
||||||
service information should also not happen in a view. Yet here we are.
|
|
||||||
*/
|
|
||||||
class ServicesView: NSView, XibLoadable {
|
|
||||||
|
|
||||||
@IBOutlet weak var imageViewPhp: NSImageView!
|
|
||||||
@IBOutlet weak var imageViewNginx: NSImageView!
|
|
||||||
@IBOutlet weak var imageViewDnsmasq: NSImageView!
|
|
||||||
|
|
||||||
@IBOutlet weak var textFieldPhp: NSTextField!
|
|
||||||
|
|
||||||
static var services: [String: HomebrewService] = [:]
|
|
||||||
|
|
||||||
static func asMenuItem() -> NSMenuItem {
|
|
||||||
let view = Self.createFromXib()!
|
|
||||||
[view.imageViewPhp, view.imageViewNginx, view.imageViewDnsmasq].forEach { imageView in
|
|
||||||
imageView?.contentTintColor = NSColor(named: "IconColorNormal")
|
|
||||||
}
|
|
||||||
let item = NSMenuItem()
|
|
||||||
item.view = view
|
|
||||||
item.target = self
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
view, selector: #selector(self.updateInformation),
|
|
||||||
name: Events.ServicesUpdated,
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateInformation() {
|
|
||||||
self.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadData() {
|
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
|
||||||
HomebrewService.loadAll { services in
|
|
||||||
ServicesView.services = Dictionary(uniqueKeysWithValues: services.map { ($0.name, $0) })
|
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyAllInfoFieldsFromCachedValue() {
|
|
||||||
if ServicesView.services.keys.isEmpty {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.textFieldPhp.stringValue = PhpEnv.phpInstall.formula.uppercased()
|
|
||||||
self.applyServiceStyling(PhpEnv.phpInstall.formula, self.imageViewPhp)
|
|
||||||
self.applyServiceStyling("nginx", self.imageViewNginx)
|
|
||||||
self.applyServiceStyling("dnsmasq", self.imageViewDnsmasq)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyServiceStyling(_ serviceName: String, _ imageView: NSImageView) {
|
|
||||||
if ServicesView.services[serviceName] == nil {
|
|
||||||
imageView.image = NSImage(named: "ServiceLoading")
|
|
||||||
imageView.contentTintColor = NSColor(named: "IconColorNormal")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ServicesView.services[serviceName]!.running {
|
|
||||||
imageView.image = NSImage(named: "ServiceOn")
|
|
||||||
imageView.contentTintColor = NSColor(named: "IconColorNormal")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
imageView.image = NSImage(named: "ServiceOff")
|
|
||||||
imageView.contentTintColor = NSColor(named: "IconColorRed")
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
NotificationCenter.default.removeObserver(self, name: Events.ServicesUpdated, object: nil)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,150 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner"/>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<customView wantsLayer="YES" id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="330" height="46"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
|
||||||
<rect key="frame" x="30" y="3" width="270" height="40"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
|
||||||
<rect key="frame" x="0.0" y="4" width="77" height="32"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
|
|
||||||
<rect key="frame" x="23" y="18" width="31" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="PHP" id="LKe-C4-jxo">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tko-cP-XSz">
|
|
||||||
<rect key="frame" x="26" y="0.0" width="24" height="16"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="16" id="Fxu-6h-A2h"/>
|
|
||||||
<constraint firstAttribute="width" constant="24" id="hOc-Ur-dmA"/>
|
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="vjB-6Z-3xR"/>
|
|
||||||
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
|
||||||
<rect key="frame" x="97" y="4" width="76" height="32"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
|
||||||
<rect key="frame" x="18" y="18" width="40" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="NGINX" id="Qfq-Bl-yuh">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZqW-6d-vpe">
|
|
||||||
<rect key="frame" x="30" y="0.0" width="16" height="16"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="16" id="EPG-jm-7Xs"/>
|
|
||||||
<constraint firstAttribute="width" constant="16" id="iif-kT-phn"/>
|
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="JmQ-dU-ip7"/>
|
|
||||||
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
|
||||||
<rect key="frame" x="193" y="4" width="77" height="32"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
|
||||||
<rect key="frame" x="8" y="18" width="62" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="DNSMASQ" id="lGh-MT-TgI">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DcG-x3-lvy">
|
|
||||||
<rect key="frame" x="31" y="0.0" width="16" height="16"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="16" id="AKl-Gq-RtM"/>
|
|
||||||
<constraint firstAttribute="height" constant="16" id="q2g-Ua-eIJ"/>
|
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ServiceLoading" id="Ign-Cq-DKf"/>
|
|
||||||
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="40" id="2EU-Fd-hMg"/>
|
|
||||||
<constraint firstItem="nWj-33-m8Q" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="CAY-Pw-B8n"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="doH-ww-BDw" secondAttribute="bottom" constant="4" id="Dq4-M6-1Wf"/>
|
|
||||||
<constraint firstItem="g4d-4N-NkC" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="bls-fM-H4b"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="nWj-33-m8Q" secondAttribute="bottom" constant="4" id="f6j-eI-wiH"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="g4d-4N-NkC" secondAttribute="bottom" constant="4" id="faS-Mo-Qa2"/>
|
|
||||||
<constraint firstItem="doH-ww-BDw" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="gL3-5S-OKo"/>
|
|
||||||
</constraints>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="TnH-dX-qaQ" secondAttribute="trailing" constant="30" id="3dD-wf-5pS"/>
|
|
||||||
<constraint firstItem="TnH-dX-qaQ" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="3" id="JmY-D0-uAy"/>
|
|
||||||
<constraint firstItem="TnH-dX-qaQ" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="30" id="S8i-CD-j3h"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="TnH-dX-qaQ" secondAttribute="bottom" constant="3" id="fDc-OY-YL0"/>
|
|
||||||
<constraint firstItem="TnH-dX-qaQ" firstAttribute="centerY" secondItem="c22-O7-iKe" secondAttribute="centerY" id="fFF-rl-3s4"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<outlet property="imageViewDnsmasq" destination="DcG-x3-lvy" id="XxJ-kZ-bdO"/>
|
|
||||||
<outlet property="imageViewNginx" destination="ZqW-6d-vpe" id="Wil-Ug-8Kb"/>
|
|
||||||
<outlet property="imageViewPhp" destination="tko-cP-XSz" id="q7L-HK-7Pj"/>
|
|
||||||
<outlet property="textFieldPhp" destination="At1-ch-qv2" id="Guk-hr-f1T"/>
|
|
||||||
</connections>
|
|
||||||
<point key="canvasLocation" x="-64" y="195"/>
|
|
||||||
</customView>
|
|
||||||
</objects>
|
|
||||||
<resources>
|
|
||||||
<image name="ServiceLoading" width="17" height="16"/>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
@@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// StatsView.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 04/02/2021.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class StatsView: NSView, XibLoadable {
|
|
||||||
|
|
||||||
@IBOutlet weak var titleMemLimit: NSTextField!
|
|
||||||
@IBOutlet weak var titleMaxPost: NSTextField!
|
|
||||||
@IBOutlet weak var titleMaxUpload: NSTextField!
|
|
||||||
|
|
||||||
@IBOutlet weak var labelMemLimit: NSTextField!
|
|
||||||
@IBOutlet weak var labelMaxPost: NSTextField!
|
|
||||||
@IBOutlet weak var labelMaxUpload: NSTextField!
|
|
||||||
|
|
||||||
static func asMenuItem(memory: String, post: String, upload: String) -> NSMenuItem {
|
|
||||||
let view = Self.createFromXib()
|
|
||||||
view!.titleMemLimit.stringValue = "mi_memory_limit".localized.uppercased()
|
|
||||||
view!.titleMaxPost.stringValue = "mi_post_max_size".localized.uppercased()
|
|
||||||
view!.titleMaxUpload.stringValue = "mi_upload_max_filesize".localized.uppercased()
|
|
||||||
view!.labelMemLimit.stringValue = memory
|
|
||||||
view!.labelMaxPost.stringValue = post
|
|
||||||
view!.labelMaxUpload.stringValue = upload
|
|
||||||
let item = NSMenuItem()
|
|
||||||
item.view = view
|
|
||||||
item.target = self
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,144 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="macosx"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<objects>
|
|
||||||
<customObject id="-2" userLabel="File's Owner"/>
|
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
|
||||||
<customView id="c22-O7-iKe" customClass="StatsView" customModule="PHP_Monitor" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="330" height="55"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
|
||||||
<rect key="frame" x="30" y="6" width="270" height="43"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
|
||||||
<rect key="frame" x="0.0" y="4" width="87" height="35"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
|
|
||||||
<rect key="frame" x="-2" y="21" width="91" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="MEMORY LIMIT" id="LKe-C4-jxo">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Emt-m3-Dt6">
|
|
||||||
<rect key="frame" x="16" y="0.0" width="55" height="19"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="H6T-wY-PIG">
|
|
||||||
<font key="font" metaFont="systemMedium" size="16"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
|
||||||
<rect key="frame" x="107" y="4" width="68" height="35"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
|
||||||
<rect key="frame" x="3" y="21" width="63" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="MAX POST" id="Qfq-Bl-yuh">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vyu-AO-8SH">
|
|
||||||
<rect key="frame" x="7" y="0.0" width="55" height="19"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="uH4-Zy-43x">
|
|
||||||
<font key="font" metaFont="systemMedium" size="16"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
|
||||||
<rect key="frame" x="195" y="4" width="75" height="35"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
|
||||||
<rect key="frame" x="-2" y="21" width="79" height="14"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="MAX UPLOAD" id="lGh-MT-TgI">
|
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eHT-tr-Kwx">
|
|
||||||
<rect key="frame" x="10" y="0.0" width="55" height="19"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="1iA-Ri-zYY">
|
|
||||||
<font key="font" metaFont="systemMedium" size="16"/>
|
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="nWj-33-m8Q" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="CAY-Pw-B8n"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="doH-ww-BDw" secondAttribute="bottom" constant="4" id="Dq4-M6-1Wf"/>
|
|
||||||
<constraint firstItem="g4d-4N-NkC" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="bls-fM-H4b"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="nWj-33-m8Q" secondAttribute="bottom" constant="4" id="f6j-eI-wiH"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="g4d-4N-NkC" secondAttribute="bottom" constant="4" id="faS-Mo-Qa2"/>
|
|
||||||
<constraint firstItem="doH-ww-BDw" firstAttribute="top" secondItem="TnH-dX-qaQ" secondAttribute="top" constant="4" id="gL3-5S-OKo"/>
|
|
||||||
</constraints>
|
|
||||||
<visibilityPriorities>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
<integer value="1000"/>
|
|
||||||
</visibilityPriorities>
|
|
||||||
<customSpacing>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
<real value="3.4028234663852886e+38"/>
|
|
||||||
</customSpacing>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="TnH-dX-qaQ" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="6" id="1mo-iG-Z0D"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="TnH-dX-qaQ" secondAttribute="trailing" constant="30" id="3dD-wf-5pS"/>
|
|
||||||
<constraint firstItem="TnH-dX-qaQ" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="30" id="S8i-CD-j3h"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="TnH-dX-qaQ" secondAttribute="bottom" constant="6" id="eve-qD-gUH"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<outlet property="labelMaxPost" destination="Vyu-AO-8SH" id="5Cm-QO-hJQ"/>
|
|
||||||
<outlet property="labelMaxUpload" destination="eHT-tr-Kwx" id="5pK-FD-c4h"/>
|
|
||||||
<outlet property="labelMemLimit" destination="Emt-m3-Dt6" id="6nD-Su-XZ6"/>
|
|
||||||
<outlet property="titleMaxPost" destination="7um-XA-djV" id="5MN-Xb-XwL"/>
|
|
||||||
<outlet property="titleMaxUpload" destination="Oef-6n-9QI" id="Q61-JI-RJq"/>
|
|
||||||
<outlet property="titleMemLimit" destination="At1-ch-qv2" id="SQT-B9-sWS"/>
|
|
||||||
</connections>
|
|
||||||
<point key="canvasLocation" x="139" y="168"/>
|
|
||||||
</customView>
|
|
||||||
</objects>
|
|
||||||
</document>
|
|
259
phpmon/Domain/Menu/StatusMenu+Items.swift
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
//
|
||||||
|
// StatusMenu+Items.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 30/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension StatusMenu {
|
||||||
|
|
||||||
|
// MARK: Remaining Menu Items
|
||||||
|
|
||||||
|
func addConfigurationMenuItems() {
|
||||||
|
self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized))
|
||||||
|
self.addItem(
|
||||||
|
NSMenuItem(title: "mi_php_config".localized,
|
||||||
|
action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c")
|
||||||
|
)
|
||||||
|
self.addItem(
|
||||||
|
NSMenuItem(title: "mi_phpmon_config".localized,
|
||||||
|
action: #selector(MainMenu.openPhpMonitorConfigurationFile), keyEquivalent: "y")
|
||||||
|
)
|
||||||
|
self.addItem(
|
||||||
|
NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addComposerMenuItems() {
|
||||||
|
self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized))
|
||||||
|
self.addItem(
|
||||||
|
NSMenuItem(title: "mi_global_composer".localized,
|
||||||
|
action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")
|
||||||
|
)
|
||||||
|
|
||||||
|
let composerMenuItem = NSMenuItem(
|
||||||
|
title: "mi_update_global_composer".localized,
|
||||||
|
action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateGlobalComposerDependencies),
|
||||||
|
keyEquivalent: "g"
|
||||||
|
)
|
||||||
|
composerMenuItem.keyEquivalentModifierMask = .shift
|
||||||
|
|
||||||
|
self.addItem(composerMenuItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addStatsMenuItem() {
|
||||||
|
guard let stats = PhpEnv.phpInstall.limits else { return }
|
||||||
|
|
||||||
|
self.addItem(StatsView.asMenuItem(
|
||||||
|
memory: stats.memory_limit,
|
||||||
|
post: stats.post_max_size,
|
||||||
|
upload: stats.upload_max_filesize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addExtensionsMenuItems() {
|
||||||
|
self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized))
|
||||||
|
|
||||||
|
if PhpEnv.phpInstall.extensions.isEmpty {
|
||||||
|
self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortcutKey = 1
|
||||||
|
for phpExtension in PhpEnv.phpInstall.extensions {
|
||||||
|
self.addExtensionItem(phpExtension, shortcutKey)
|
||||||
|
shortcutKey += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPresetsMenuItem() {
|
||||||
|
guard let presets = Preferences.custom.presets else {
|
||||||
|
addEmptyPresetHelp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if presets.isEmpty {
|
||||||
|
addEmptyPresetHelp()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addLoadedPresets()
|
||||||
|
}
|
||||||
|
|
||||||
|
func addEmptyPresetHelp() {
|
||||||
|
let presets = NSMenuItem(title: "mi_presets_title".localized, action: nil, keyEquivalent: "")
|
||||||
|
|
||||||
|
let presetsMenu = NSMenu()
|
||||||
|
|
||||||
|
presetsMenu.addItem(NSMenuItem(title: "mi_no_presets".localized, action: nil, keyEquivalent: ""))
|
||||||
|
presetsMenu.addItem(NSMenuItem.separator())
|
||||||
|
presetsMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_set_up_presets".localized,
|
||||||
|
action: #selector(MainMenu.showPresetHelp),
|
||||||
|
keyEquivalent: "")
|
||||||
|
)
|
||||||
|
|
||||||
|
presetsMenu.items.forEach { $0.target = MainMenu.shared }
|
||||||
|
|
||||||
|
self.setSubmenu(presetsMenu, for: presets)
|
||||||
|
self.addItem(presets)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLoadedPresets() {
|
||||||
|
let presets = NSMenuItem(title: "mi_presets_title".localized, action: nil, keyEquivalent: "")
|
||||||
|
|
||||||
|
let presetsMenu = NSMenu()
|
||||||
|
|
||||||
|
presetsMenu.addItem(NSMenuItem.separator())
|
||||||
|
presetsMenu.addItem(HeaderView.asMenuItem(text: "mi_apply_presets_title".localized))
|
||||||
|
|
||||||
|
for preset in Preferences.custom.presets! {
|
||||||
|
let presetMenuItem = PresetMenuItem(
|
||||||
|
title: preset.getMenuItemText(),
|
||||||
|
action: #selector(MainMenu.togglePreset(sender:)),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
|
||||||
|
if let attributedString = try? NSMutableAttributedString(
|
||||||
|
data: preset.getMenuItemText().data(using: .utf8)!,
|
||||||
|
options: [.documentType: NSAttributedString.DocumentType.html],
|
||||||
|
documentAttributes: nil
|
||||||
|
) {
|
||||||
|
presetMenuItem.attributedTitle = attributedString
|
||||||
|
}
|
||||||
|
|
||||||
|
presetMenuItem.preset = preset
|
||||||
|
presetsMenu.addItem(presetMenuItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
presetsMenu.addItem(NSMenuItem.separator())
|
||||||
|
presetsMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_revert_to_prev_config".localized,
|
||||||
|
action: PresetHelper.rollbackPreset != nil
|
||||||
|
? #selector(MainMenu.rollbackPreset)
|
||||||
|
: nil,
|
||||||
|
keyEquivalent: ""
|
||||||
|
))
|
||||||
|
presetsMenu.addItem(NSMenuItem.separator())
|
||||||
|
presetsMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_profiles_loaded".localized(
|
||||||
|
Preferences.custom.presets!.count
|
||||||
|
),
|
||||||
|
action: nil, keyEquivalent: "")
|
||||||
|
)
|
||||||
|
for item in presetsMenu.items {
|
||||||
|
item.target = MainMenu.shared
|
||||||
|
}
|
||||||
|
self.setSubmenu(presetsMenu, for: presets)
|
||||||
|
self.addItem(presets)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addXdebugMenuItem() {
|
||||||
|
if !Xdebug.enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addItem(NSMenuItem.separator())
|
||||||
|
let xdebugSwitch = NSMenuItem(
|
||||||
|
title: "mi_xdebug_mode".localized,
|
||||||
|
action: nil,
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
let xdebugModesMenu = NSMenu()
|
||||||
|
let activeModes = Xdebug.activeModes
|
||||||
|
|
||||||
|
xdebugModesMenu.addItem(HeaderView.asMenuItem(text: "mi_xdebug_available_modes".localized))
|
||||||
|
|
||||||
|
for mode in Xdebug.modes {
|
||||||
|
let item = XdebugMenuItem(
|
||||||
|
title: mode,
|
||||||
|
action: #selector(MainMenu.toggleXdebugMode(sender:)),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
|
||||||
|
item.state = activeModes.contains(mode) ? .on : .off
|
||||||
|
item.mode = mode
|
||||||
|
xdebugModesMenu.addItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
xdebugModesMenu.addItem(HeaderView.asMenuItem(text: "mi_xdebug_actions".localized))
|
||||||
|
xdebugModesMenu.addItem(
|
||||||
|
withTitle: "mi_xdebug_disable_all".localized,
|
||||||
|
action: #selector(MainMenu.disableAllXdebugModes),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in xdebugModesMenu.items {
|
||||||
|
item.target = MainMenu.shared
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubmenu(xdebugModesMenu, for: xdebugSwitch)
|
||||||
|
self.addItem(xdebugSwitch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFirstAidAndServicesMenuItems() {
|
||||||
|
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||||
|
let servicesMenu = NSMenu()
|
||||||
|
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_view_onboarding".localized,
|
||||||
|
action: #selector(MainMenu.showWelcomeTour), keyEquivalent: ""))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_fa_php_doctor".localized,
|
||||||
|
action: #selector(MainMenu.openWarnings), keyEquivalent: ""))
|
||||||
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
let fixMyValetMenuItem = NSMenuItem(
|
||||||
|
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||||
|
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
|
||||||
|
)
|
||||||
|
fixMyValetMenuItem.toolTip = "mi_fix_my_valet_tooltip".localized
|
||||||
|
servicesMenu.addItem(fixMyValetMenuItem)
|
||||||
|
|
||||||
|
let fixHomebrewMenuItem = NSMenuItem(
|
||||||
|
title: "mi_fix_brew_permissions".localized(),
|
||||||
|
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: ""
|
||||||
|
)
|
||||||
|
fixHomebrewMenuItem.toolTip = "mi_fix_brew_permissions_tooltip".localized
|
||||||
|
servicesMenu.addItem(fixHomebrewMenuItem)
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized,
|
||||||
|
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized,
|
||||||
|
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized,
|
||||||
|
action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_valet_services".localized,
|
||||||
|
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s"))
|
||||||
|
servicesMenu.addItem(
|
||||||
|
NSMenuItem(title: "mi_stop_valet_services".localized,
|
||||||
|
action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"),
|
||||||
|
withKeyModifier: [.command, .shift]
|
||||||
|
)
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_manual_actions".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(
|
||||||
|
NSMenuItem(title: "mi_php_refresh".localized,
|
||||||
|
action: #selector(MainMenu.reloadPhpMonitorMenuInForeground), keyEquivalent: "r")
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in servicesMenu.items {
|
||||||
|
item.target = MainMenu.shared
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubmenu(servicesMenu, for: services)
|
||||||
|
self.addItem(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -66,15 +66,40 @@ class StatusMenu: NSMenu {
|
|||||||
|
|
||||||
self.addExtensionsMenuItems()
|
self.addExtensionsMenuItems()
|
||||||
|
|
||||||
|
self.addXdebugMenuItem()
|
||||||
|
|
||||||
|
self.addPhpDoctorMenuItem()
|
||||||
|
|
||||||
self.addItem(NSMenuItem.separator())
|
self.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
// self.addXdebugMenuItem()
|
self.addPresetsMenuItem()
|
||||||
|
|
||||||
self.addFirstAidAndServicesMenuItems()
|
self.addFirstAidAndServicesMenuItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addPhpDoctorMenuItem() {
|
||||||
|
if !Preferences.isEnabled(.showPhpDoctorSuggestions) ||
|
||||||
|
!WarningManager.shared.hasWarnings() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addItem(NSMenuItem.separator())
|
||||||
|
self.addItem(HeaderView.asMenuItem(text: "mi_php_doctor".localized))
|
||||||
|
self.addItem(NSMenuItem(
|
||||||
|
title: "mi_recommendations_count".localized(WarningManager.shared.warnings.count),
|
||||||
|
action: nil,
|
||||||
|
keyEquivalent: ""
|
||||||
|
))
|
||||||
|
self.addItem(NSMenuItem(
|
||||||
|
title: "mi_view_recommendations".localized,
|
||||||
|
action: #selector(MainMenu.openWarnings),
|
||||||
|
keyEquivalent: ""
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
func addCoreMenuItems() {
|
func addCoreMenuItems() {
|
||||||
self.addItem(NSMenuItem.separator())
|
self.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
self.addItem(NSMenuItem(title: "mi_preferences".localized,
|
self.addItem(NSMenuItem(title: "mi_preferences".localized,
|
||||||
action: #selector(MainMenu.openPrefs), keyEquivalent: ","))
|
action: #selector(MainMenu.openPrefs), keyEquivalent: ","))
|
||||||
self.addItem(NSMenuItem(title: "mi_check_for_updates".localized,
|
self.addItem(NSMenuItem(title: "mi_check_for_updates".localized,
|
||||||
@@ -86,154 +111,9 @@ class StatusMenu: NSMenu {
|
|||||||
action: #selector(MainMenu.terminateApp), keyEquivalent: "q"))
|
action: #selector(MainMenu.terminateApp), keyEquivalent: "q"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Remaining Menu Items
|
|
||||||
|
|
||||||
func addConfigurationMenuItems() {
|
|
||||||
self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized))
|
|
||||||
self.addItem(
|
|
||||||
NSMenuItem(title: "mi_php_config".localized,
|
|
||||||
action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c")
|
|
||||||
)
|
|
||||||
self.addItem(
|
|
||||||
NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addComposerMenuItems() {
|
|
||||||
self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized))
|
|
||||||
self.addItem(
|
|
||||||
NSMenuItem(title: "mi_global_composer".localized,
|
|
||||||
action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")
|
|
||||||
)
|
|
||||||
|
|
||||||
let composerMenuItem = NSMenuItem(
|
|
||||||
title: "mi_update_global_composer".localized,
|
|
||||||
action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateGlobalComposerDependencies),
|
|
||||||
keyEquivalent: "g"
|
|
||||||
)
|
|
||||||
composerMenuItem.keyEquivalentModifierMask = .shift
|
|
||||||
|
|
||||||
self.addItem(composerMenuItem)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addStatsMenuItem() {
|
|
||||||
guard let stats = PhpEnv.phpInstall.limits else { return }
|
|
||||||
|
|
||||||
self.addItem(StatsView.asMenuItem(
|
|
||||||
memory: stats.memory_limit,
|
|
||||||
post: stats.post_max_size,
|
|
||||||
upload: stats.upload_max_filesize)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addExtensionsMenuItems() {
|
|
||||||
self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized))
|
|
||||||
|
|
||||||
if PhpEnv.phpInstall.extensions.isEmpty {
|
|
||||||
self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: ""))
|
|
||||||
}
|
|
||||||
|
|
||||||
var shortcutKey = 1
|
|
||||||
for phpExtension in PhpEnv.phpInstall.extensions {
|
|
||||||
self.addExtensionItem(phpExtension, shortcutKey)
|
|
||||||
shortcutKey += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addXdebugMenuItem() {
|
|
||||||
if !Xdebug.enabled {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let xdebugSwitch = NSMenuItem(
|
|
||||||
title: "mi_xdebug_mode".localized,
|
|
||||||
action: nil,
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
let xdebugModesMenu = NSMenu()
|
|
||||||
let xdebugMode = Xdebug.mode
|
|
||||||
|
|
||||||
for mode in Xdebug.modes {
|
|
||||||
let item = XdebugMenuItem(
|
|
||||||
title: mode,
|
|
||||||
action: #selector(MainMenu.toggleXdebugMode(sender:)),
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
item.state = xdebugMode == mode ? .on : .off
|
|
||||||
item.mode = mode
|
|
||||||
xdebugModesMenu.addItem(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
for item in xdebugModesMenu.items {
|
|
||||||
item.target = MainMenu.shared
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setSubmenu(xdebugModesMenu, for: xdebugSwitch)
|
|
||||||
self.addItem(xdebugSwitch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addFirstAidAndServicesMenuItems() {
|
|
||||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
|
||||||
let servicesMenu = NSMenu()
|
|
||||||
|
|
||||||
let fixMyValetMenuItem = NSMenuItem(
|
|
||||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
|
||||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
|
|
||||||
)
|
|
||||||
fixMyValetMenuItem.toolTip = "mi_fix_my_valet_tooltip".localized
|
|
||||||
servicesMenu.addItem(fixMyValetMenuItem)
|
|
||||||
|
|
||||||
let fixHomebrewMenuItem = NSMenuItem(
|
|
||||||
title: "mi_fix_brew_permissions".localized(),
|
|
||||||
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: ""
|
|
||||||
)
|
|
||||||
fixHomebrewMenuItem.toolTip = "mi_fix_brew_permissions_tooltip".localized
|
|
||||||
servicesMenu.addItem(fixHomebrewMenuItem)
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem.separator())
|
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
|
||||||
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_dnsmasq".localized,
|
|
||||||
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_php_fpm".localized,
|
|
||||||
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_nginx".localized,
|
|
||||||
action: #selector(MainMenu.restartNginx), keyEquivalent: "n")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_all_services".localized,
|
|
||||||
action: #selector(MainMenu.restartAllServices), keyEquivalent: "s")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_stop_all_services".localized,
|
|
||||||
action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"),
|
|
||||||
withKeyModifier: [.command, .shift]
|
|
||||||
)
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem.separator())
|
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_manual_actions".localized))
|
|
||||||
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_php_refresh".localized,
|
|
||||||
action: #selector(MainMenu.reloadPhpMonitorMenuInForeground), keyEquivalent: "r")
|
|
||||||
)
|
|
||||||
|
|
||||||
for item in servicesMenu.items {
|
|
||||||
item.target = MainMenu.shared
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setSubmenu(servicesMenu, for: services)
|
|
||||||
self.addItem(services)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Private Helpers
|
// MARK: Private Helpers
|
||||||
|
|
||||||
private func addSwitchToPhpMenuItems() {
|
internal func addSwitchToPhpMenuItems() {
|
||||||
var shortcutKey = 1
|
var shortcutKey = 1
|
||||||
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
|
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
|
||||||
|
|
||||||
@@ -260,7 +140,7 @@ class StatusMenu: NSMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addExtensionItem(_ phpExtension: PhpExtension, _ shortcutKey: Int) {
|
internal func addExtensionItem(_ phpExtension: PhpExtension, _ shortcutKey: Int) {
|
||||||
let keyEquivalent = shortcutKey < 9 ? "\(shortcutKey)" : ""
|
let keyEquivalent = shortcutKey < 9 ? "\(shortcutKey)" : ""
|
||||||
|
|
||||||
let menuItem = ExtensionMenuItem(
|
let menuItem = ExtensionMenuItem(
|
||||||
@@ -279,21 +159,3 @@ class StatusMenu: NSMenu {
|
|||||||
self.addItem(menuItem)
|
self.addItem(menuItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - NSMenuItem subclasses
|
|
||||||
|
|
||||||
class PhpMenuItem: NSMenuItem {
|
|
||||||
var version: String = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
class XdebugMenuItem: NSMenuItem {
|
|
||||||
var mode: String = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExtensionMenuItem: NSMenuItem {
|
|
||||||
var phpExtension: PhpExtension?
|
|
||||||
}
|
|
||||||
|
|
||||||
class EditorMenuItem: NSMenuItem {
|
|
||||||
var editor: Application?
|
|
||||||
}
|
|
||||||
|
@@ -91,6 +91,7 @@ class BetterAlert {
|
|||||||
|
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
windowController.window?.makeKeyAndOrderFront(nil)
|
windowController.window?.makeKeyAndOrderFront(nil)
|
||||||
|
windowController.window?.setCenterPosition(offsetY: 70)
|
||||||
return NSApplication.shared.runModal(for: windowController.window!)
|
return NSApplication.shared.runModal(for: windowController.window!)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,14 +103,14 @@ class BetterAlert {
|
|||||||
/**
|
/**
|
||||||
Shows the modal and does not return anything.
|
Shows the modal and does not return anything.
|
||||||
*/
|
*/
|
||||||
public func show() {
|
@MainActor public func show() {
|
||||||
_ = self.runModal()
|
_ = self.runModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Shows the modal for a particular error.
|
Shows the modal for a particular error.
|
||||||
*/
|
*/
|
||||||
public static func show(for error: Error & AlertableError) {
|
@MainActor public static func show(for error: Error & AlertableError) {
|
||||||
let key = error.getErrorMessageKey()
|
let key = error.getErrorMessageKey()
|
||||||
return BetterAlert().withInformation(
|
return BetterAlert().withInformation(
|
||||||
title: "\(key).title".localized,
|
title: "\(key).title".localized,
|
||||||
|
45
phpmon/Domain/Onboarding/OnboardingWindowController.swift
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// OnboardingWindowController.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 25/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
class OnboardingWindowController: PMWindowController {
|
||||||
|
|
||||||
|
// MARK: - Window Identifier
|
||||||
|
|
||||||
|
override var windowName: String {
|
||||||
|
return "Onboarding"
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func create(delegate: NSWindowDelegate?) {
|
||||||
|
let windowController = Self()
|
||||||
|
windowController.window = NSWindow()
|
||||||
|
|
||||||
|
guard let window = windowController.window else { return }
|
||||||
|
window.title = ""
|
||||||
|
window.styleMask = [.titled, .closable, .miniaturizable]
|
||||||
|
window.titlebarAppearsTransparent = true
|
||||||
|
window.delegate = delegate ?? windowController
|
||||||
|
window.contentView = NSHostingView(rootView: OnboardingView())
|
||||||
|
window.setContentSize(NSSize(width: 600, height: 600))
|
||||||
|
|
||||||
|
App.shared.onboardingWindowController = windowController
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func show(delegate: NSWindowDelegate? = nil) {
|
||||||
|
if App.shared.onboardingWindowController == nil {
|
||||||
|
Self.create(delegate: delegate)
|
||||||
|
}
|
||||||
|
|
||||||
|
App.shared.onboardingWindowController?.showWindow(self)
|
||||||
|
App.shared.onboardingWindowController?.window?.setCenterPosition(offsetY: 70)
|
||||||
|
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
|
}
|
@@ -10,8 +10,32 @@ import Foundation
|
|||||||
|
|
||||||
struct CustomPrefs: Decodable {
|
struct CustomPrefs: Decodable {
|
||||||
let scanApps: [String]
|
let scanApps: [String]
|
||||||
|
let presets: [Preset]?
|
||||||
|
let services: [String]?
|
||||||
|
let environmentVariables: [String: String]?
|
||||||
|
|
||||||
|
public func hasPresets() -> Bool {
|
||||||
|
return self.presets != nil && !self.presets!.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hasServices() -> Bool {
|
||||||
|
return self.services != nil && !self.services!.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hasEnvironmentVariables() -> Bool {
|
||||||
|
return self.environmentVariables != nil && !self.environmentVariables!.keys.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getEnvironmentVariables() -> String {
|
||||||
|
return self.environmentVariables!.map { (key, value) in
|
||||||
|
return "export \(key)=\(value)"
|
||||||
|
}.joined(separator: "&&")
|
||||||
|
}
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case scanApps = "scan_apps"
|
case scanApps = "scan_apps"
|
||||||
|
case presets = "presets"
|
||||||
|
case services = "services"
|
||||||
|
case environmentVariables = "export"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
phpmon/Domain/Preferences/Keys.swift
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Keys.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 25/07/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Keys {
|
||||||
|
static let Escape = 53
|
||||||
|
static let Space = 49
|
||||||
|
}
|
@@ -12,15 +12,29 @@ import Foundation
|
|||||||
These are the keys used for every preference in the app.
|
These are the keys used for every preference in the app.
|
||||||
*/
|
*/
|
||||||
enum PreferenceName: String {
|
enum PreferenceName: String {
|
||||||
|
// FIRST-TIME LAUNCH
|
||||||
case wasLaunchedBefore = "launched_before"
|
case wasLaunchedBefore = "launched_before"
|
||||||
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
|
||||||
case iconTypeToDisplay = "icon_type_to_display"
|
// GENERAL
|
||||||
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
|
||||||
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
||||||
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
|
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
|
||||||
case allowProtocolForIntegrations = "allow_protocol_for_integrations"
|
case allowProtocolForIntegrations = "allow_protocol_for_integrations"
|
||||||
case globalHotkey = "global_hotkey"
|
case globalHotkey = "global_hotkey"
|
||||||
case automaticBackgroundUpdateCheck = "backgroundUpdateCheck"
|
case automaticBackgroundUpdateCheck = "backgroundUpdateCheck"
|
||||||
|
case showPhpDoctorSuggestions = "show_php_doctor_suggestions"
|
||||||
|
|
||||||
|
// APPEARANCE
|
||||||
|
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
||||||
|
case iconTypeToDisplay = "icon_type_to_display"
|
||||||
|
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
||||||
|
|
||||||
|
// NOTIFICATIONS
|
||||||
|
case notifyAboutVersionChange = "notify_about_version_change"
|
||||||
|
case notifyAboutPhpFpmRestart = "notify_about_php_fpm_restart"
|
||||||
|
case notifyAboutServices = "notify_about_services_restart"
|
||||||
|
case notifyAboutPresets = "notify_about_presets"
|
||||||
|
case notifyAboutSecureToggle = "notify_about_secure_toggle"
|
||||||
|
case notifyAboutGlobalComposerStatus = "notify_about_composer_status"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +66,7 @@ class Preferences {
|
|||||||
public init() {
|
public init() {
|
||||||
Preferences.handleFirstTimeLaunch()
|
Preferences.handleFirstTimeLaunch()
|
||||||
cachedPreferences = Self.cache()
|
cachedPreferences = Self.cache()
|
||||||
customPreferences = CustomPrefs(scanApps: [])
|
customPreferences = CustomPrefs(scanApps: [], presets: [], services: [], environmentVariables: [:])
|
||||||
loadCustomPreferences()
|
loadCustomPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,14 +84,26 @@ class Preferences {
|
|||||||
*/
|
*/
|
||||||
static func handleFirstTimeLaunch() {
|
static func handleFirstTimeLaunch() {
|
||||||
UserDefaults.standard.register(defaults: [
|
UserDefaults.standard.register(defaults: [
|
||||||
/// Preferences
|
/// Preferences: General
|
||||||
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
|
||||||
PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue,
|
|
||||||
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
|
||||||
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
|
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
|
||||||
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
|
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
|
||||||
PreferenceName.allowProtocolForIntegrations.rawValue: true,
|
PreferenceName.allowProtocolForIntegrations.rawValue: true,
|
||||||
PreferenceName.automaticBackgroundUpdateCheck.rawValue: true,
|
PreferenceName.automaticBackgroundUpdateCheck.rawValue: true,
|
||||||
|
PreferenceName.showPhpDoctorSuggestions.rawValue: true,
|
||||||
|
|
||||||
|
/// Preferences: Appearance
|
||||||
|
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
||||||
|
PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue,
|
||||||
|
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
||||||
|
|
||||||
|
/// Preferences: Notifications
|
||||||
|
PreferenceName.notifyAboutVersionChange.rawValue: true,
|
||||||
|
PreferenceName.notifyAboutPhpFpmRestart.rawValue: true,
|
||||||
|
PreferenceName.notifyAboutServices.rawValue: true,
|
||||||
|
PreferenceName.notifyAboutPresets.rawValue: true,
|
||||||
|
PreferenceName.notifyAboutSecureToggle.rawValue: true,
|
||||||
|
PreferenceName.notifyAboutGlobalComposerStatus.rawValue: true,
|
||||||
|
|
||||||
/// Stats
|
/// Stats
|
||||||
InternalStats.switchCount.rawValue: 0,
|
InternalStats.switchCount.rawValue: 0,
|
||||||
InternalStats.launchCount.rawValue: 0,
|
InternalStats.launchCount.rawValue: 0,
|
||||||
@@ -137,7 +163,8 @@ class Preferences {
|
|||||||
private static func cache() -> [PreferenceName: Any] {
|
private static func cache() -> [PreferenceName: Any] {
|
||||||
return [
|
return [
|
||||||
// Part 1: Always Booleans
|
// Part 1: Always Booleans
|
||||||
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(
|
.shouldDisplayDynamicIcon:
|
||||||
|
UserDefaults.standard.bool(
|
||||||
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
||||||
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(
|
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(
|
||||||
forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
||||||
@@ -149,6 +176,21 @@ class Preferences {
|
|||||||
forKey: PreferenceName.allowProtocolForIntegrations.rawValue) as Any,
|
forKey: PreferenceName.allowProtocolForIntegrations.rawValue) as Any,
|
||||||
.automaticBackgroundUpdateCheck: UserDefaults.standard.bool(
|
.automaticBackgroundUpdateCheck: UserDefaults.standard.bool(
|
||||||
forKey: PreferenceName.automaticBackgroundUpdateCheck.rawValue) as Any,
|
forKey: PreferenceName.automaticBackgroundUpdateCheck.rawValue) as Any,
|
||||||
|
.showPhpDoctorSuggestions: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.showPhpDoctorSuggestions.rawValue) as Any,
|
||||||
|
|
||||||
|
.notifyAboutVersionChange: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutVersionChange.rawValue) as Any,
|
||||||
|
.notifyAboutPhpFpmRestart: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutPhpFpmRestart.rawValue) as Any,
|
||||||
|
.notifyAboutServices: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutServices.rawValue) as Any,
|
||||||
|
.notifyAboutPresets: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutPresets.rawValue) as Any,
|
||||||
|
.notifyAboutSecureToggle: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutSecureToggle.rawValue) as Any,
|
||||||
|
.notifyAboutGlobalComposerStatus: UserDefaults.standard.bool(
|
||||||
|
forKey: PreferenceName.notifyAboutGlobalComposerStatus.rawValue) as Any,
|
||||||
|
|
||||||
// Part 2: Always Strings
|
// Part 2: Always Strings
|
||||||
.globalHotkey: UserDefaults.standard.string(
|
.globalHotkey: UserDefaults.standard.string(
|
||||||
@@ -173,12 +215,28 @@ class Preferences {
|
|||||||
// MARK: - Custom Preferences
|
// MARK: - Custom Preferences
|
||||||
|
|
||||||
private func loadCustomPreferences() {
|
private func loadCustomPreferences() {
|
||||||
let url = URL(fileURLWithPath: "/Users/\(Paths.whoami)/.phpmon.conf.json")
|
// Ensure the configuration directory is created if missing
|
||||||
|
Shell.run("mkdir -p ~/.config/phpmon")
|
||||||
|
|
||||||
|
// Move the legacy file
|
||||||
|
moveOutdatedConfigurationFile()
|
||||||
|
|
||||||
|
// Attempt to load the file if it exists
|
||||||
|
let url = URL(fileURLWithPath: "\(Paths.homePath)/.config/phpmon/config.json")
|
||||||
if Filesystem.fileExists(url.path) {
|
if Filesystem.fileExists(url.path) {
|
||||||
Log.info("A custom .phpmon.conf.json file was found. Attempting to parse...")
|
|
||||||
|
Log.info("A custom ~/.config/phpmon/config.json file was found. Attempting to parse...")
|
||||||
loadCustomPreferencesFile(url)
|
loadCustomPreferencesFile(url)
|
||||||
} else {
|
} else {
|
||||||
Log.info("There was no .phpmon.conf.json file to be loaded.")
|
Log.info("There was no /.config/phpmon/config.json file to be loaded.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func moveOutdatedConfigurationFile() {
|
||||||
|
if Filesystem.fileExists("~/.phpmon.conf.json") && !Filesystem.fileExists("~/.config/phpmon/config.json") {
|
||||||
|
Log.info("An outdated configuration file was found. Moving it...")
|
||||||
|
Shell.run("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
|
||||||
|
Log.info("The configuration file was copied successfully!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,9 +246,23 @@ class Preferences {
|
|||||||
CustomPrefs.self,
|
CustomPrefs.self,
|
||||||
from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)!
|
from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)!
|
||||||
)
|
)
|
||||||
Log.info("The .phpmon.conf.json file was successfully parsed.")
|
|
||||||
|
Log.info("The ~/.config/phpmon/config.json file was successfully parsed.")
|
||||||
|
|
||||||
|
if customPreferences.hasPresets() {
|
||||||
|
Log.info("There are \(customPreferences.presets!.count) custom presets.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if customPreferences.hasServices() {
|
||||||
|
Log.info("There are custom services: \(customPreferences.services!)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if customPreferences.hasEnvironmentVariables() {
|
||||||
|
Log.info("Configuring the additional exports...")
|
||||||
|
Shell.user.exports = customPreferences.getEnvironmentVariables()
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
Log.warn("The .phpmon.conf.json file seems to be missing or malformed.")
|
Log.warn("The ~/.config/phpmon/config.json file seems to be missing or malformed.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// PreferencesWindowController+Hotkey.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 25/07/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension PreferencesWindowController {
|
||||||
|
|
||||||
|
// MARK: - Key Interaction
|
||||||
|
|
||||||
|
override func keyDown(with event: NSEvent) {
|
||||||
|
super.keyDown(with: event)
|
||||||
|
|
||||||
|
guard let tabVC = self.contentViewController as? NSTabViewController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let vc = tabVC.tabViewItems[tabVC.selectedTabViewItemIndex].viewController as? GenericPreferenceVC else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if vc.listeningForHotkeyView == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if event.keyCode == Keys.Escape || event.keyCode == Keys.Space {
|
||||||
|
Log.info("A blacklisted key was pressed, canceling listen!")
|
||||||
|
vc.listeningForHotkeyView!.unregister(nil)
|
||||||
|
} else {
|
||||||
|
vc.listeningForHotkeyView!.updateShortcut(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
phpmon/Domain/Preferences/PreferencesWindowController.swift
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// PreferencesWindowController.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 02/04/2021.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class PreferencesWindowController: PMWindowController {
|
||||||
|
|
||||||
|
// MARK: - Window Identifier
|
||||||
|
|
||||||
|
override var windowName: String {
|
||||||
|
return "Preferences"
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func create(delegate: NSWindowDelegate?) {
|
||||||
|
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
|
let windowController = storyboard.instantiateController(
|
||||||
|
withIdentifier: "preferencesWindow"
|
||||||
|
) as! PreferencesWindowController
|
||||||
|
|
||||||
|
guard let window = windowController.window else { return }
|
||||||
|
|
||||||
|
window.title = "prefs.title".localized
|
||||||
|
window.subtitle = "prefs.subtitle".localized
|
||||||
|
window.delegate = delegate ?? windowController
|
||||||
|
window.styleMask = [.titled, .closable, .miniaturizable]
|
||||||
|
|
||||||
|
App.shared.preferencesWindowController = windowController
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func show(delegate: NSWindowDelegate? = nil) {
|
||||||
|
var justCreated = false
|
||||||
|
|
||||||
|
if App.shared.preferencesWindowController == nil {
|
||||||
|
Self.create(delegate: delegate)
|
||||||
|
|
||||||
|
guard let preferencesWC = App.shared.preferencesWindowController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let tabVC = preferencesWC.contentViewController as? NSTabViewController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for vc in preferencesWC.tabVCs {
|
||||||
|
tabVC.addChild(vc.viewController)
|
||||||
|
let item = tabVC.tabViewItem(for: vc.viewController)
|
||||||
|
item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "\(vc.label) Icon")
|
||||||
|
item?.label = vc.label
|
||||||
|
}
|
||||||
|
|
||||||
|
tabVC.preferredContentSize = NSSize(
|
||||||
|
width: tabVC.view.frame.size.width,
|
||||||
|
height: tabVC.view.frame.size.height
|
||||||
|
)
|
||||||
|
|
||||||
|
justCreated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
App.shared.preferencesWindowController?.showWindow(self)
|
||||||
|
|
||||||
|
if justCreated {
|
||||||
|
App.shared.preferencesWindowController?.positionWindowInTopLeftCorner()
|
||||||
|
}
|
||||||
|
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Tabs
|
||||||
|
|
||||||
|
struct PrefTabView {
|
||||||
|
let viewController: GenericPreferenceVC
|
||||||
|
let label: String
|
||||||
|
let icon: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public lazy var tabVCs: [PrefTabView] = {
|
||||||
|
return [
|
||||||
|
PrefTabView(
|
||||||
|
viewController: GeneralPreferencesVC.fromStoryboard(),
|
||||||
|
label: "General",
|
||||||
|
icon: "gearshape"
|
||||||
|
),
|
||||||
|
PrefTabView(
|
||||||
|
viewController: AppearancePreferencesVC.fromStoryboard(),
|
||||||
|
label: "Appearance",
|
||||||
|
icon: "paintbrush"
|
||||||
|
),
|
||||||
|
PrefTabView(
|
||||||
|
viewController: NotificationPreferencesVC.fromStoryboard(),
|
||||||
|
label: "Notifications",
|
||||||
|
icon: "bell.badge"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
@@ -9,56 +9,26 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import Carbon
|
import Carbon
|
||||||
|
|
||||||
class PrefsVC: NSViewController {
|
class GenericPreferenceVC: NSViewController {
|
||||||
|
|
||||||
// MARK: - Window Identifier
|
// MARK: - Content
|
||||||
|
|
||||||
@IBOutlet weak var stackView: NSStackView!
|
@IBOutlet weak var stackView: NSStackView!
|
||||||
|
|
||||||
// MARK: - Display
|
var views: [NSView] = []
|
||||||
|
|
||||||
public static func create(delegate: NSWindowDelegate?) {
|
|
||||||
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
|
||||||
|
|
||||||
let windowController = storyboard.instantiateController(
|
|
||||||
withIdentifier: "preferencesWindow"
|
|
||||||
) as! PrefsWC
|
|
||||||
|
|
||||||
windowController.window!.title = "prefs.title".localized
|
|
||||||
windowController.window!.subtitle = "prefs.subtitle".localized
|
|
||||||
windowController.window!.delegate = delegate
|
|
||||||
windowController.window!.styleMask = [.titled, .closable, .miniaturizable]
|
|
||||||
windowController.window!.delegate = windowController
|
|
||||||
windowController.positionWindowInTopLeftCorner()
|
|
||||||
|
|
||||||
App.shared.preferencesWindowController = windowController
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func show(delegate: NSWindowDelegate? = nil) {
|
|
||||||
if App.shared.preferencesWindowController == nil {
|
|
||||||
Self.create(delegate: delegate)
|
|
||||||
}
|
|
||||||
|
|
||||||
App.shared.preferencesWindowController!.showWindow(self)
|
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
[
|
super.viewDidLoad()
|
||||||
getDynamicIconPreferenceView(),
|
self.views.forEach({ self.stackView.addArrangedSubview($0) })
|
||||||
getIconOptionsPreferenceView(),
|
|
||||||
getIconDensityPreferenceView(),
|
|
||||||
getAutoRestartPreferenceView(),
|
|
||||||
getAutomaticComposerUpdatePreferenceView(),
|
|
||||||
getShortcutPreferenceView(),
|
|
||||||
getIntegrationsPreferenceView(),
|
|
||||||
getAutomaticUpdateCheckPreferenceView()
|
|
||||||
].forEach({ self.stackView.addArrangedSubview($0) })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getDynamicIconPreferenceView() -> NSView {
|
// MARK: - Deinitialization
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Log.perf("PrefsVC deallocated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDynamicIconPV() -> NSView {
|
||||||
return CheckboxPreferenceView.make(
|
return CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.dynamic_icon".localized,
|
sectionText: "prefs.dynamic_icon".localized,
|
||||||
descriptionText: "prefs.dynamic_icon_desc".localized,
|
descriptionText: "prefs.dynamic_icon_desc".localized,
|
||||||
@@ -70,7 +40,7 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getIconOptionsPreferenceView() -> NSView {
|
func getIconOptionsPV() -> NSView {
|
||||||
return SelectPreferenceView.make(
|
return SelectPreferenceView.make(
|
||||||
sectionText: "",
|
sectionText: "",
|
||||||
descriptionText: "prefs.icon_options_desc".localized,
|
descriptionText: "prefs.icon_options_desc".localized,
|
||||||
@@ -83,7 +53,7 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getIconDensityPreferenceView() -> NSView {
|
func getIconDensityPV() -> NSView {
|
||||||
return CheckboxPreferenceView.make(
|
return CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.info_density".localized,
|
sectionText: "prefs.info_density".localized,
|
||||||
descriptionText: "prefs.display_full_php_version_desc".localized,
|
descriptionText: "prefs.display_full_php_version_desc".localized,
|
||||||
@@ -96,7 +66,7 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAutoRestartPreferenceView() -> NSView {
|
func getAutoRestartPV() -> NSView {
|
||||||
return CheckboxPreferenceView.make(
|
return CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.services".localized,
|
sectionText: "prefs.services".localized,
|
||||||
descriptionText: "prefs.auto_restart_services_desc".localized,
|
descriptionText: "prefs.auto_restart_services_desc".localized,
|
||||||
@@ -106,7 +76,7 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAutomaticComposerUpdatePreferenceView() -> NSView {
|
func getAutomaticComposerUpdatePV() -> NSView {
|
||||||
CheckboxPreferenceView.make(
|
CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.switcher".localized,
|
sectionText: "prefs.switcher".localized,
|
||||||
descriptionText: "prefs.auto_composer_update_desc".localized,
|
descriptionText: "prefs.auto_composer_update_desc".localized,
|
||||||
@@ -116,15 +86,15 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getShortcutPreferenceView() -> NSView {
|
func getShortcutPV() -> NSView {
|
||||||
return HotkeyPreferenceView.make(
|
return HotkeyPreferenceView.make(
|
||||||
sectionText: "prefs.global_shortcut".localized,
|
sectionText: "prefs.global_shortcut".localized,
|
||||||
descriptionText: "prefs.shortcut_desc".localized,
|
descriptionText: "prefs.shortcut_desc".localized,
|
||||||
self
|
self
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getIntegrationsPreferenceView() -> NSView {
|
func getIntegrationsPV() -> NSView {
|
||||||
return CheckboxPreferenceView.make(
|
return CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.integrations".localized,
|
sectionText: "prefs.integrations".localized,
|
||||||
descriptionText: "prefs.open_protocol_desc".localized,
|
descriptionText: "prefs.open_protocol_desc".localized,
|
||||||
@@ -134,7 +104,7 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getAutomaticUpdateCheckPreferenceView() -> NSView {
|
func getAutomaticUpdateCheckPV() -> NSView {
|
||||||
return CheckboxPreferenceView.make(
|
return CheckboxPreferenceView.make(
|
||||||
sectionText: "prefs.updates".localized,
|
sectionText: "prefs.updates".localized,
|
||||||
descriptionText: "prefs.automatic_update_check_desc".localized,
|
descriptionText: "prefs.automatic_update_check_desc".localized,
|
||||||
@@ -144,6 +114,80 @@ class PrefsVC: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getShowPhpDoctorSuggestionsPV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "prefs.php_doctor".localized,
|
||||||
|
descriptionText: "prefs.php_doctor_suggestions_desc".localized,
|
||||||
|
checkboxText: "prefs.php_doctor_suggestions_title".localized,
|
||||||
|
preference: .showPhpDoctorSuggestions,
|
||||||
|
action: {
|
||||||
|
MainMenu.shared.refreshIcon()
|
||||||
|
MainMenu.shared.rebuild()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutVersionChangePV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "prefs.notifications".localized,
|
||||||
|
descriptionText: "prefs.notify_about_version_change_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_version_change".localized,
|
||||||
|
preference: .notifyAboutVersionChange,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutPhpFpmChangePV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "",
|
||||||
|
descriptionText: "prefs.notify_about_php_fpm_change_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_php_fpm_change".localized,
|
||||||
|
preference: .notifyAboutPhpFpmRestart,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutServicesPV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "",
|
||||||
|
descriptionText: "prefs.notify_about_services_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_services".localized,
|
||||||
|
preference: .notifyAboutServices,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutPresetsPV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "",
|
||||||
|
descriptionText: "prefs.notify_about_presets_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_presets".localized,
|
||||||
|
preference: .notifyAboutPresets,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutSecureTogglePV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "",
|
||||||
|
descriptionText: "prefs.notify_about_secure_status_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_secure_status".localized,
|
||||||
|
preference: .notifyAboutSecureToggle,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNotifyAboutGlobalComposerStatusPV() -> NSView {
|
||||||
|
return CheckboxPreferenceView.make(
|
||||||
|
sectionText: "",
|
||||||
|
descriptionText: "prefs.notify_about_composer_success_desc".localized,
|
||||||
|
checkboxText: "prefs.notify_about_composer_success".localized,
|
||||||
|
preference: .notifyAboutGlobalComposerStatus,
|
||||||
|
action: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Listening for hotkey delegate
|
// MARK: - Listening for hotkey delegate
|
||||||
|
|
||||||
var listeningForHotkeyView: HotkeyPreferenceView?
|
var listeningForHotkeyView: HotkeyPreferenceView?
|
||||||
@@ -153,10 +197,63 @@ class PrefsVC: NSViewController {
|
|||||||
listeningForHotkeyView = nil
|
listeningForHotkeyView = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Deinitialization
|
class GeneralPreferencesVC: GenericPreferenceVC {
|
||||||
|
|
||||||
deinit {
|
// MARK: - Lifecycle
|
||||||
Log.perf("PrefsVC deallocated")
|
|
||||||
|
public static func fromStoryboard() -> GenericPreferenceVC {
|
||||||
|
let vc = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
|
||||||
|
|
||||||
|
vc.views = [
|
||||||
|
vc.getShowPhpDoctorSuggestionsPV(),
|
||||||
|
vc.getAutoRestartPV(),
|
||||||
|
vc.getAutomaticComposerUpdatePV(),
|
||||||
|
vc.getShortcutPV(),
|
||||||
|
vc.getIntegrationsPV(),
|
||||||
|
vc.getAutomaticUpdateCheckPV()
|
||||||
|
]
|
||||||
|
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationPreferencesVC: GenericPreferenceVC {
|
||||||
|
|
||||||
|
public static func fromStoryboard() -> GenericPreferenceVC {
|
||||||
|
let vc = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
|
||||||
|
|
||||||
|
vc.views = [
|
||||||
|
vc.getNotifyAboutVersionChangePV(),
|
||||||
|
vc.getNotifyAboutPresetsPV(),
|
||||||
|
vc.getNotifyAboutSecureTogglePV(),
|
||||||
|
vc.getNotifyAboutGlobalComposerStatusPV(),
|
||||||
|
vc.getNotifyAboutServicesPV(),
|
||||||
|
vc.getNotifyAboutPhpFpmChangePV()
|
||||||
|
]
|
||||||
|
|
||||||
|
return vc
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppearancePreferencesVC: GenericPreferenceVC {
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
public static func fromStoryboard() -> GenericPreferenceVC {
|
||||||
|
let vc = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
|
||||||
|
|
||||||
|
vc.views = [
|
||||||
|
vc.getDynamicIconPV(),
|
||||||
|
vc.getIconOptionsPV(),
|
||||||
|
vc.getIconDensityPV()
|
||||||
|
]
|
||||||
|
|
||||||
|
return vc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// PrefsWC.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 02/04/2021.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
struct Keys {
|
|
||||||
static let Escape = 53
|
|
||||||
static let Space = 49
|
|
||||||
}
|
|
||||||
|
|
||||||
class PrefsWC: PMWindowController {
|
|
||||||
|
|
||||||
// MARK: - Window Identifier
|
|
||||||
|
|
||||||
override var windowName: String {
|
|
||||||
return "Preferences"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Key Interaction
|
|
||||||
|
|
||||||
override func keyDown(with event: NSEvent) {
|
|
||||||
super.keyDown(with: event)
|
|
||||||
|
|
||||||
if let vc = contentViewController as? PrefsVC {
|
|
||||||
if vc.listeningForHotkeyView != nil {
|
|
||||||
if event.keyCode == Keys.Escape || event.keyCode == Keys.Space {
|
|
||||||
Log.info("A blacklisted key was pressed, canceling listen!")
|
|
||||||
vc.listeningForHotkeyView = nil
|
|
||||||
} else {
|
|
||||||
vc.listeningForHotkeyView!.updateShortcut(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -11,7 +11,7 @@ import Cocoa
|
|||||||
|
|
||||||
class HotkeyPreferenceView: NSView, XibLoadable {
|
class HotkeyPreferenceView: NSView, XibLoadable {
|
||||||
|
|
||||||
weak var delegate: PrefsVC?
|
weak var delegate: GenericPreferenceVC?
|
||||||
|
|
||||||
@IBOutlet weak var labelSection: NSTextField!
|
@IBOutlet weak var labelSection: NSTextField!
|
||||||
@IBOutlet weak var labelDescription: NSTextField!
|
@IBOutlet weak var labelDescription: NSTextField!
|
||||||
@@ -19,7 +19,7 @@ class HotkeyPreferenceView: NSView, XibLoadable {
|
|||||||
@IBOutlet weak var buttonSetShortcut: NSButton!
|
@IBOutlet weak var buttonSetShortcut: NSButton!
|
||||||
@IBOutlet weak var buttonClearShortcut: NSButton!
|
@IBOutlet weak var buttonClearShortcut: NSButton!
|
||||||
|
|
||||||
static func make(sectionText: String, descriptionText: String, _ prefsVC: PrefsVC) -> NSView {
|
static func make(sectionText: String, descriptionText: String, _ prefsVC: GenericPreferenceVC) -> NSView {
|
||||||
let view = Self.createFromXib()!
|
let view = Self.createFromXib()!
|
||||||
view.labelSection.stringValue = sectionText
|
view.labelSection.stringValue = sectionText
|
||||||
view.labelDescription.stringValue = descriptionText
|
view.labelDescription.stringValue = descriptionText
|
||||||
|
271
phpmon/Domain/Presets/Preset.swift
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
//
|
||||||
|
// Preset.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 31/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Preset: Codable, Equatable {
|
||||||
|
let name: String
|
||||||
|
let version: String?
|
||||||
|
let extensions: [String: Bool]
|
||||||
|
let configuration: [String: String?]
|
||||||
|
|
||||||
|
public enum CodingKeys: String, CodingKey {
|
||||||
|
case version = "php",
|
||||||
|
name = "name",
|
||||||
|
extensions = "extensions",
|
||||||
|
configuration = "configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
What the preset does, in text form. Used to display what's going on.
|
||||||
|
*/
|
||||||
|
var textDescription: String {
|
||||||
|
var text = ""
|
||||||
|
|
||||||
|
if self.version != nil {
|
||||||
|
text += "alert.preset_description.switcher_version".localized(self.version!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.extensions.isEmpty {
|
||||||
|
// Show a subsection header
|
||||||
|
text += "alert.preset_description.applying_extensions".localized
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ext, extValue) in self.extensions {
|
||||||
|
// An extension is either enabled or disabled
|
||||||
|
let status = extValue
|
||||||
|
? "alert.preset_description.enabled".localized
|
||||||
|
: "alert.preset_description.disabled".localized
|
||||||
|
text += "• \(ext): \(status)\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.configuration.isEmpty {
|
||||||
|
// Extra spacing if the previous section was extensions
|
||||||
|
if !self.extensions.isEmpty {
|
||||||
|
text += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a subsection header
|
||||||
|
text += "alert.preset_description.applying_config".localized
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, value) in self.configuration {
|
||||||
|
// A value is either displayed, or the value is "(empty)"
|
||||||
|
text += "• \(key)=\(value ?? "alert.preset_description.empty".localized) \n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Applying
|
||||||
|
|
||||||
|
/**
|
||||||
|
Applies a given preset.
|
||||||
|
*/
|
||||||
|
public func apply() {
|
||||||
|
Task {
|
||||||
|
// Was this a rollback?
|
||||||
|
let wasRollback = (self.name == "AutomaticRevertSnapshot")
|
||||||
|
|
||||||
|
// Save the preset that would revert this preset
|
||||||
|
self.persistRevert()
|
||||||
|
|
||||||
|
// Apply the PHP version if is considered a valid version
|
||||||
|
if self.version != nil {
|
||||||
|
if await !switchToPhpVersionIfValid() {
|
||||||
|
PresetHelper.rollbackPreset = nil
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the configuration changes first
|
||||||
|
for conf in configuration {
|
||||||
|
applyConfigurationValue(key: conf.key, value: conf.value ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the extension changes in-place afterward
|
||||||
|
for ext in extensions {
|
||||||
|
for foundExt in PhpEnv.phpInstall.extensions
|
||||||
|
where foundExt.name == ext.key && foundExt.enabled != ext.value {
|
||||||
|
Log.info("Toggling extension \(foundExt.name) in \(foundExt.file)")
|
||||||
|
foundExt.toggle()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload what rollback file exists
|
||||||
|
PresetHelper.loadRollbackPresetFromFile()
|
||||||
|
|
||||||
|
// Restart PHP FPM process (also reloads menu, which will show the preset rollback)
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
|
||||||
|
// Show the correct notification
|
||||||
|
if wasRollback {
|
||||||
|
await LocalNotification.send(
|
||||||
|
title: "notification.preset_reverted_title".localized,
|
||||||
|
subtitle: "notification.preset_reverted_desc".localized,
|
||||||
|
preference: .notifyAboutPresets
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await LocalNotification.send(
|
||||||
|
title: "notification.preset_applied_title".localized,
|
||||||
|
subtitle: "notification.preset_applied_desc".localized(self.name),
|
||||||
|
preference: .notifyAboutPresets
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Apply Functionality
|
||||||
|
|
||||||
|
private func switchToPhpVersionIfValid() async -> Bool {
|
||||||
|
if PhpEnv.shared.currentInstall.version.short == self.version! {
|
||||||
|
Log.info("The version we are supposed to switch to is already active.")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if PhpEnv.shared.availablePhpVersions.first(where: { $0 == self.version }) != nil {
|
||||||
|
await MainMenu.shared.switchToPhp(self.version!)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "alert.php_switch_unavailable.title".localized,
|
||||||
|
subtitle: "alert.php_switch_unavailable.subtitle".localized(version!),
|
||||||
|
description: "alert.php_switch_unavailable.info".localized(
|
||||||
|
version!,
|
||||||
|
PhpEnv.shared.availablePhpVersions.joined(separator: ", ")
|
||||||
|
)
|
||||||
|
).withPrimary(
|
||||||
|
text: "alert.php_switch_unavailable.ok".localized
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func applyConfigurationValue(key: String, value: String) {
|
||||||
|
guard let file = PhpEnv.shared.getConfigFile(forKey: key) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if file.has(key: key) {
|
||||||
|
Log.info("Setting config value \(key) in \(file.filePath)")
|
||||||
|
try file.replace(key: key, value: value)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Log.err("Setting \(key) to \(value) failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Menu Items
|
||||||
|
|
||||||
|
public func getMenuItemText() -> String {
|
||||||
|
var info = extensions.count == 1
|
||||||
|
? "preset.extension".localized(extensions.count)
|
||||||
|
: "preset.extensions".localized(extensions.count)
|
||||||
|
info += ", "
|
||||||
|
info += configuration.count == 1
|
||||||
|
? "preset.preference".localized(configuration.count)
|
||||||
|
: "preset.preferences".localized(configuration.count)
|
||||||
|
|
||||||
|
if self.version == nil {
|
||||||
|
return "<span style=\"font-family: '-apple-system'; font-size: 12px;\">"
|
||||||
|
+ "<b>\(name.stripped)</b><br/>"
|
||||||
|
+ "<i style=\"font-size: 11px;\">"
|
||||||
|
+ info + "</i>"
|
||||||
|
+ "</span>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<span style=\"font-family: '-apple-system'; font-size: 12px;\">"
|
||||||
|
+ "<b>\(name.stripped)</b><br/>"
|
||||||
|
+ "<i style=\"font-size: 11px;\">"
|
||||||
|
+ "Switches to PHP \(version!)<br/>"
|
||||||
|
+ info + "</i>"
|
||||||
|
+ "</span>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Reverting
|
||||||
|
|
||||||
|
public var revertSnapshot: Preset {
|
||||||
|
return Preset(
|
||||||
|
name: "AutomaticRevertSnapshot",
|
||||||
|
version: diffVersion(),
|
||||||
|
extensions: diffExtensions(),
|
||||||
|
configuration: diffConfiguration()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the version that was previously active, which would revert this preset's version.
|
||||||
|
Returns nil if the version is not specified or the same.
|
||||||
|
*/
|
||||||
|
private func diffVersion() -> String? {
|
||||||
|
guard let version = self.version else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if PhpEnv.shared.currentInstall.version.short != version {
|
||||||
|
return PhpEnv.shared.currentInstall.version.short
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a list of extensions which would revert this presets's setup.
|
||||||
|
*/
|
||||||
|
private func diffExtensions() -> [String: Bool] {
|
||||||
|
var items: [String: Bool] = [:]
|
||||||
|
|
||||||
|
for (key, value) in self.extensions {
|
||||||
|
for foundExt in PhpEnv.phpInstall.extensions
|
||||||
|
where foundExt.name == key && foundExt.enabled != value {
|
||||||
|
// Save the original value of the extension
|
||||||
|
items[foundExt.name] = foundExt.enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns a list of configuration items which would revert this presets's setup.
|
||||||
|
*/
|
||||||
|
private func diffConfiguration() -> [String: String?] {
|
||||||
|
var items: [String: String?] = [:]
|
||||||
|
|
||||||
|
for (key, _) in self.configuration {
|
||||||
|
guard let file = PhpEnv.shared.getConfigFile(forKey: key) else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
items[key] = file.get(for: key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Persists the revert as a JSON file, so it can be read from a file after restarting PHP Monitor.
|
||||||
|
*/
|
||||||
|
private func persistRevert() {
|
||||||
|
let data = try! JSONEncoder().encode(self.revertSnapshot)
|
||||||
|
|
||||||
|
Shell.run("mkdir -p ~/.config/phpmon")
|
||||||
|
|
||||||
|
try! String(data: data, encoding: .utf8)!
|
||||||
|
.write(
|
||||||
|
toFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
|
||||||
|
atomically: true,
|
||||||
|
encoding: .utf8
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
37
phpmon/Domain/Presets/PresetHelper.swift
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// PresetHelper.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 02/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PresetHelper {
|
||||||
|
|
||||||
|
static var rollbackPreset: Preset?
|
||||||
|
|
||||||
|
// MARK: - Reloading Configuration
|
||||||
|
|
||||||
|
public static func loadRollbackPresetFromFile() {
|
||||||
|
guard let revert = try? String(
|
||||||
|
contentsOfFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
|
||||||
|
encoding: .utf8
|
||||||
|
) else {
|
||||||
|
PresetHelper.rollbackPreset = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let preset = try? JSONDecoder().decode(
|
||||||
|
Preset.self,
|
||||||
|
from: revert.data(using: .utf8)!
|
||||||
|
) else {
|
||||||
|
PresetHelper.rollbackPreset = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PresetHelper.rollbackPreset = preset
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
phpmon/Domain/Progress/ProgressVC.swift
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// ProgressVC.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 26/07/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class ProgressViewController: NSViewController {
|
||||||
|
|
||||||
|
@IBOutlet weak var labelTitle: NSTextField!
|
||||||
|
@IBOutlet weak var labelDescription: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet var textView: NSTextView!
|
||||||
|
@IBOutlet weak var imageViewType: NSImageView!
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Log.perf("Deinitializing ProgressViewController")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -1,15 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="pUZ-6w-gUX">
|
<scene sceneID="pUZ-6w-gUX">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController storyboardIdentifier="progressWindow" id="LSr-Iw-X1T" customClass="ProgressWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController storyboardIdentifier="progressWindow" id="LSr-Iw-X1T" customClass="TerminalProgressWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="none" frameAutosaveName="" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="PD9-0p-i0S" customClass="NSPanel">
|
<window key="window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="none" frameAutosaveName="" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="PD9-0p-i0S" customClass="NSPanel">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" rightStrut="YES" topStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" rightStrut="YES" topStrut="YES"/>
|
||||||
@@ -43,7 +42,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
||||||
<clipView key="contentView" drawsBackground="NO" id="2Mc-oy-AzN">
|
<clipView key="contentView" drawsBackground="NO" id="2Mc-oy-AzN">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="d1T-N1-CRe">
|
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="d1T-N1-CRe">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
|
||||||
@@ -65,11 +64,11 @@
|
|||||||
</textView>
|
</textView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</clipView>
|
</clipView>
|
||||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="51v-CN-AuA">
|
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="51v-CN-AuA">
|
||||||
<rect key="frame" x="-100" y="-100" width="225" height="15"/>
|
<rect key="frame" x="-100" y="-100" width="225" height="15"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="lSO-JG-QOf">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="lSO-JG-QOf">
|
||||||
<rect key="frame" x="-100" y="-100" width="15" height="173"/>
|
<rect key="frame" x="-100" y="-100" width="15" height="173"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ProgressView.swift
|
// TerminalProgressWindowController.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 18/12/2021.
|
// Created by Nico Verbruggen on 18/12/2021.
|
||||||
@@ -9,14 +9,14 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class ProgressWindowController: NSWindowController, NSWindowDelegate {
|
class TerminalProgressWindowController: NSWindowController, NSWindowDelegate {
|
||||||
|
|
||||||
static func display(title: String, description: String) -> ProgressWindowController {
|
static func display(title: String, description: String) -> TerminalProgressWindowController {
|
||||||
let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil)
|
let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil)
|
||||||
|
|
||||||
let windowController = storyboard.instantiateController(
|
let windowController = storyboard.instantiateController(
|
||||||
withIdentifier: "progressWindow"
|
withIdentifier: "progressWindow"
|
||||||
) as! ProgressWindowController
|
) as! TerminalProgressWindowController
|
||||||
|
|
||||||
windowController.showWindow(windowController)
|
windowController.showWindow(windowController)
|
||||||
windowController.window?.makeKeyAndOrderFront(nil)
|
windowController.window?.makeKeyAndOrderFront(nil)
|
||||||
@@ -60,17 +60,3 @@ class ProgressWindowController: NSWindowController, NSWindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProgressViewController: NSViewController {
|
|
||||||
|
|
||||||
@IBOutlet weak var labelTitle: NSTextField!
|
|
||||||
@IBOutlet weak var labelDescription: NSTextField!
|
|
||||||
|
|
||||||
@IBOutlet var textView: NSTextView!
|
|
||||||
@IBOutlet weak var imageViewType: NSImageView!
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
Log.perf("Deinitializing ProgressViewController")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
27
phpmon/Domain/SwiftUI/Common/SwiftUIHelper.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// SwiftUIHelper.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
var isRunningSwiftUIPreview: Bool {
|
||||||
|
return ProcessInfo.processInfo
|
||||||
|
.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
public static var appPrimary: Color = Color("AppColor")
|
||||||
|
public static var appSecondary: Color = Color("AppSecondary")
|
||||||
|
|
||||||
|
public static var debug: Color = {
|
||||||
|
if ProcessInfo.processInfo.environment["PAINT_PHPMON_SWIFTUI_VIEWS"] != nil {
|
||||||
|
return Color.yellow
|
||||||
|
}
|
||||||
|
return Color.clear
|
||||||
|
}()
|
||||||
|
}
|
195
phpmon/Domain/SwiftUI/Domains/VersionPopoverView.swift
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
//
|
||||||
|
// VersionPopoverView.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct VersionPopoverView: View {
|
||||||
|
|
||||||
|
@State var site: ValetSite
|
||||||
|
|
||||||
|
@State var validPhpVersions: [PhpVersionNumber]
|
||||||
|
|
||||||
|
@State var parent: NSPopover!
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
|
Text(getTitleText())
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
Text(getSourceText())
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
.font(.subheadline)
|
||||||
|
if !validPhpVersions.isEmpty {
|
||||||
|
// Suggestions for alternative PHP versions
|
||||||
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
|
DisclaimerView(
|
||||||
|
iconName: "info.circle.fill",
|
||||||
|
message: "alert.php_suggestions".localized,
|
||||||
|
color: Color("AppColor")
|
||||||
|
)
|
||||||
|
HStack {
|
||||||
|
ForEach(validPhpVersions, id: \.self) { version in
|
||||||
|
Button("site_link.switch_to_php".localized(version.homebrewVersion), action: {
|
||||||
|
MainMenu.shared.switchToPhpVersion(version.homebrewVersion)
|
||||||
|
parent?.close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}.padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if site.composerPhpSource == .unknown {
|
||||||
|
// We don't know which PHP version is required
|
||||||
|
DisclaimerView(
|
||||||
|
iconName: "questionmark.circle.fill",
|
||||||
|
message: "alert.unable_to_determine_is_fine".localized
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if site.composerPhpCompatibleWithLinked {
|
||||||
|
DisclaimerView(
|
||||||
|
iconName: "checkmark.circle.fill",
|
||||||
|
message: "alert.php_version_ideal".localized,
|
||||||
|
color: Color("IconColorGreen")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
DisclaimerView(
|
||||||
|
iconName: "exclamationmark.circle.fill",
|
||||||
|
message: "alert.php_version_incorrect".localized,
|
||||||
|
color: Color("IconColorRed")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.frame(width: 400, height: nil, alignment: .center)
|
||||||
|
.padding(20)
|
||||||
|
.background(
|
||||||
|
Color(NSColor.windowBackgroundColor)
|
||||||
|
.padding(-80)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTitleText() -> String {
|
||||||
|
if site.composerPhpSource == .unknown {
|
||||||
|
return "alert.composer_php_requirement.unable_to_determine".localized
|
||||||
|
}
|
||||||
|
|
||||||
|
return "alert.composer_php_requirement.title".localized(
|
||||||
|
"\(site.name).\(Valet.shared.config.tld)",
|
||||||
|
site.composerPhp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSourceText() -> String {
|
||||||
|
var information = ""
|
||||||
|
|
||||||
|
if site.isolatedPhpVersion != nil {
|
||||||
|
information += "alert.composer_php_isolated.desc".localized(
|
||||||
|
site.isolatedPhpVersion!.versionNumber.homebrewVersion,
|
||||||
|
PhpEnv.phpInstall.version.short
|
||||||
|
)
|
||||||
|
information += "\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
|
||||||
|
.localized
|
||||||
|
|
||||||
|
return information
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DisclaimerView: View {
|
||||||
|
@State var iconName: String
|
||||||
|
@State var message: String
|
||||||
|
@State var color: Color = Color.secondary
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(alignment: .firstTextBaseline, spacing: 5) {
|
||||||
|
Image(systemName: iconName)
|
||||||
|
.renderingMode(.template)
|
||||||
|
.foregroundColor(color)
|
||||||
|
Text(message)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(color)
|
||||||
|
}.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VersionPopoverView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VersionPopoverView(
|
||||||
|
site: ValetSite(
|
||||||
|
fakeWithName: "amazingwebsite",
|
||||||
|
tld: "test",
|
||||||
|
secure: true,
|
||||||
|
path: "/path/to/site",
|
||||||
|
linked: true,
|
||||||
|
constraint: ""
|
||||||
|
),
|
||||||
|
validPhpVersions: [],
|
||||||
|
parent: nil
|
||||||
|
)
|
||||||
|
.previewDisplayName("Unknown Requirement")
|
||||||
|
|
||||||
|
VersionPopoverView(
|
||||||
|
site: ValetSite(
|
||||||
|
fakeWithName: "amazingwebsite",
|
||||||
|
tld: "test",
|
||||||
|
secure: true,
|
||||||
|
path: "/path/to/site",
|
||||||
|
linked: true,
|
||||||
|
constraint: "^8.1"
|
||||||
|
),
|
||||||
|
validPhpVersions: [],
|
||||||
|
parent: nil
|
||||||
|
)
|
||||||
|
.previewDisplayName("Requirement Matches")
|
||||||
|
VersionPopoverView(
|
||||||
|
site: ValetSite(
|
||||||
|
fakeWithName: "anothersite",
|
||||||
|
tld: "test",
|
||||||
|
secure: true,
|
||||||
|
path: "/path/to/site",
|
||||||
|
linked: true,
|
||||||
|
constraint: "^8.0",
|
||||||
|
isolated: "8.0"
|
||||||
|
),
|
||||||
|
validPhpVersions: [],
|
||||||
|
parent: nil
|
||||||
|
)
|
||||||
|
.previewDisplayName("Isolated")
|
||||||
|
VersionPopoverView(
|
||||||
|
site: ValetSite(
|
||||||
|
fakeWithName: "anothersite",
|
||||||
|
tld: "test",
|
||||||
|
secure: true,
|
||||||
|
path: "/path/to/site",
|
||||||
|
linked: true,
|
||||||
|
constraint: "^8.0",
|
||||||
|
isolated: "7.4"
|
||||||
|
),
|
||||||
|
validPhpVersions: [],
|
||||||
|
parent: nil
|
||||||
|
)
|
||||||
|
.previewDisplayName("Isolated Mismatch")
|
||||||
|
VersionPopoverView(
|
||||||
|
site: ValetSite(
|
||||||
|
fakeWithName: "anothersite",
|
||||||
|
tld: "test",
|
||||||
|
secure: true,
|
||||||
|
path: "/path/to/site",
|
||||||
|
linked: true,
|
||||||
|
constraint: "^8.0"
|
||||||
|
),
|
||||||
|
validPhpVersions: [
|
||||||
|
PhpVersionNumber(major: 8, minor: 0, patch: 0),
|
||||||
|
PhpVersionNumber(major: 8, minor: 1, patch: 0)
|
||||||
|
],
|
||||||
|
parent: nil
|
||||||
|
)
|
||||||
|
.previewDisplayName("Recommend Alternatives")
|
||||||
|
}
|
||||||
|
}
|
47
phpmon/Domain/SwiftUI/Menu/HeaderView.swift
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// MiniHeaderView.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct HeaderView: View {
|
||||||
|
@State var text: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(text.uppercased())
|
||||||
|
.font(.system(size: 12))
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(.appSecondary)
|
||||||
|
.multilineTextAlignment(.leading)
|
||||||
|
.padding(.leading, 14.0)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.background(Color.debug)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSMenuItem
|
||||||
|
|
||||||
|
static func asMenuItem(
|
||||||
|
text: String,
|
||||||
|
width: Int? = nil
|
||||||
|
) -> NSMenuItem {
|
||||||
|
let view = NSHostingView(rootView: Self(text: text))
|
||||||
|
view.autoresizingMask = [.width, .height]
|
||||||
|
view.setFrameSize(CGSize(width: view.frame.width, height: 24))
|
||||||
|
|
||||||
|
let item = NSMenuItem()
|
||||||
|
item.view = view
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HeaderView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
HeaderView(text: "Hello world")
|
||||||
|
.frame(width: 330.0)
|
||||||
|
}
|
||||||
|
}
|
22
phpmon/Domain/SwiftUI/Menu/SectionHeaderView.swift
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// MiniHeaderView.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/06/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SectionHeaderView: View {
|
||||||
|
|
||||||
|
@State var text: String
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Text(text)
|
||||||
|
.font(.system(size: 11))
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.appSecondary)
|
||||||
|
.background(Color.debug)
|
||||||
|
}
|
||||||
|
}
|