🚀 Version 5.2
Merge branch 'dev/5.x'
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Nico Verbruggen
|
Copyright (c) 2019-2022 Nico Verbruggen
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 52;
|
objectVersion = 50;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@ -11,6 +11,16 @@
|
|||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
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 */; };
|
||||||
|
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
||||||
|
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
||||||
|
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; };
|
||||||
|
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; };
|
||||||
|
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; };
|
||||||
|
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; };
|
||||||
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; };
|
||||||
|
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; };
|
||||||
|
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; };
|
||||||
|
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; };
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
@ -52,6 +62,10 @@
|
|||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A527E60D7A009F26CB /* SiteScanner.swift */; };
|
||||||
|
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */; };
|
||||||
|
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A527E60D7A009F26CB /* SiteScanner.swift */; };
|
||||||
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */; };
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||||
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; };
|
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; };
|
||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||||
@ -66,11 +80,23 @@
|
|||||||
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 */; };
|
||||||
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 /* nicoverbruggen.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nicoverbruggen.test */; };
|
||||||
|
C42CFB1827DFDFDC00862737 /* nicoverbruggen_isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */; };
|
||||||
|
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */; };
|
||||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
||||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
||||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; };
|
C43A8A2025D9D1D700591B77 /* brew.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; };
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; };
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; };
|
||||||
|
C44067F527E2582B0045BD4E /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
||||||
|
C44067F727E258410045BD4E /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
||||||
|
C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
||||||
|
C44067FB27E25FD70045BD4E /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
||||||
|
C449B4F027EE7FB800C47E8A /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
||||||
|
C449B4F127EE7FC200C47E8A /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
||||||
|
C449B4F227EE7FC400C47E8A /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
||||||
|
C449B4F327EE7FC600C47E8A /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
||||||
|
C449B4F427EE7FC800C47E8A /* SiteListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */; };
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
||||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||||
@ -83,8 +109,7 @@
|
|||||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||||
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCell.swift */; };
|
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */; };
|
||||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCell.swift */; };
|
|
||||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.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 */; };
|
||||||
@ -106,10 +131,10 @@
|
|||||||
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 */; };
|
||||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
|
||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; };
|
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; };
|
||||||
|
C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* SiteListKindCell.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 */; };
|
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 */; };
|
||||||
@ -133,7 +158,6 @@
|
|||||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
||||||
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
||||||
C4C1019627C659B7001FACC2 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4C1019527C659B7001FACC2 /* HotKey */; };
|
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
||||||
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
||||||
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||||
@ -148,8 +172,13 @@
|
|||||||
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 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */; };
|
||||||
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.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 */; };
|
||||||
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
||||||
|
C4D936CB27E3EE4A00BD69FE /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */; };
|
||||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||||
@ -219,6 +248,13 @@
|
|||||||
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>"; };
|
||||||
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>"; };
|
||||||
|
54D9E0AD27E4F51E003B9AD9 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKey.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyCombo.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModifierFlagsExtension.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0BF27E4F5D9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||||
|
54D9E0C027E4F5E9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||||
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
@ -240,6 +276,8 @@
|
|||||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
||||||
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||||
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
||||||
|
C41C02A527E60D7A009F26CB /* SiteScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteScanner.swift; sourceTree = "<group>"; };
|
||||||
|
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ValetSite+Fake.swift"; sourceTree = "<group>"; };
|
||||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -254,17 +292,24 @@
|
|||||||
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>"; };
|
||||||
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>"; };
|
||||||
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 /* nicoverbruggen.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen.test; sourceTree = "<group>"; };
|
||||||
|
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen_isolated.test; sourceTree = "<group>"; };
|
||||||
|
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigParserTest.swift; 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>"; };
|
||||||
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
||||||
C43A8A1F25D9D1D700591B77 /* brew.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = "<group>"; };
|
C43A8A1F25D9D1D700591B77 /* brew.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = "<group>"; };
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListNameCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F627E258410045BD4E /* SiteListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListPhpCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTLSCell.swift; sourceTree = "<group>"; };
|
||||||
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; };
|
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.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>"; };
|
||||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = "<group>"; };
|
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = "<group>"; };
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = "<group>"; };
|
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCellProtocol.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>"; };
|
||||||
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>"; };
|
||||||
@ -283,6 +328,7 @@
|
|||||||
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>"; };
|
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
||||||
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
||||||
|
C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListKindCell.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>"; };
|
||||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.swift; sourceTree = "<group>"; };
|
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.swift; sourceTree = "<group>"; };
|
||||||
@ -304,8 +350,10 @@
|
|||||||
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>"; };
|
||||||
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 /* NginxConfigParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigParser.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>"; };
|
||||||
C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
|
C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
|
||||||
C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = "<group>"; };
|
C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = "<group>"; };
|
||||||
C4DEB7D327A5D60B00834718 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
C4DEB7D327A5D60B00834718 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
||||||
@ -340,7 +388,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -348,7 +395,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C4C1019627C659B7001FACC2 /* HotKey in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -383,6 +429,27 @@
|
|||||||
path = PHP;
|
path = PHP;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
54D9E0AB27E4F502003B9AD9 /* HotKey */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
54D9E0BF27E4F5D9003B9AD9 /* LICENSE */,
|
||||||
|
54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */,
|
||||||
|
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */,
|
||||||
|
54D9E0AD27E4F51E003B9AD9 /* Key.swift */,
|
||||||
|
54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */,
|
||||||
|
54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */,
|
||||||
|
);
|
||||||
|
path = HotKey;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
54D9E0BE27E4F5C0003B9AD9 /* Vendor */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
54D9E0AB27E4F502003B9AD9 /* HotKey */,
|
||||||
|
);
|
||||||
|
path = Vendor;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
54FCFD28276C88C0004CE748 /* Views */ = {
|
54FCFD28276C88C0004CE748 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -417,6 +484,8 @@
|
|||||||
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C42CFB1527DFDE7900862737 /* nicoverbruggen.test */,
|
||||||
|
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */,
|
||||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
||||||
C43A8A1F25D9D1D700591B77 /* brew.json */,
|
C43A8A1F25D9D1D700591B77 /* brew.json */,
|
||||||
C4F30B06278E195800755FCE /* brew-services.json */,
|
C4F30B06278E195800755FCE /* brew-services.json */,
|
||||||
@ -447,6 +516,7 @@
|
|||||||
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
||||||
C4E713562570150F00007428 /* SECURITY.md */,
|
C4E713562570150F00007428 /* SECURITY.md */,
|
||||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
||||||
|
54D9E0C027E4F5E9003B9AD9 /* LICENSE */,
|
||||||
C4E713572570151400007428 /* docs */,
|
C4E713572570151400007428 /* docs */,
|
||||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||||
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
||||||
@ -469,6 +539,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C4B5853A2770FE2500DA4FBE /* Common */,
|
C4B5853A2770FE2500DA4FBE /* Common */,
|
||||||
C41E181722CB61EB0072CF09 /* Domain */,
|
C41E181722CB61EB0072CF09 /* Domain */,
|
||||||
|
54D9E0BE27E4F5C0003B9AD9 /* Vendor */,
|
||||||
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
||||||
C4232EE42612526500158FC6 /* Credits.html */,
|
C4232EE42612526500158FC6 /* Credits.html */,
|
||||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */,
|
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */,
|
||||||
@ -504,6 +575,19 @@
|
|||||||
path = Domain;
|
path = Domain;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C44067F327E256560045BD4E /* Cells */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */,
|
||||||
|
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */,
|
||||||
|
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */,
|
||||||
|
C44067F627E258410045BD4E /* SiteListPhpCell.swift */,
|
||||||
|
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */,
|
||||||
|
C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */,
|
||||||
|
);
|
||||||
|
path = Cells;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44C198F276E3A380072762D /* Progress */ = {
|
C44C198F276E3A380072762D /* Progress */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -525,12 +609,12 @@
|
|||||||
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C44067F327E256560045BD4E /* Cells */,
|
||||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
||||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
||||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */,
|
|
||||||
);
|
);
|
||||||
path = SiteList;
|
path = SiteList;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -573,6 +657,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
||||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */,
|
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */,
|
||||||
|
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */,
|
||||||
);
|
);
|
||||||
path = "PHP Version";
|
path = "PHP Version";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -582,6 +667,9 @@
|
|||||||
children = (
|
children = (
|
||||||
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
||||||
C4E4404527C56F4700D225E1 /* ValetSite.swift */,
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */,
|
||||||
|
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */,
|
||||||
|
C41C02A527E60D7A009F26CB /* SiteScanner.swift */,
|
||||||
|
C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */,
|
||||||
);
|
);
|
||||||
path = Valet;
|
path = Valet;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -639,6 +727,7 @@
|
|||||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
||||||
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
||||||
|
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */,
|
||||||
);
|
);
|
||||||
path = Parsers;
|
path = Parsers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -767,7 +856,6 @@
|
|||||||
);
|
);
|
||||||
name = "PHP Monitor";
|
name = "PHP Monitor";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
C4998F0526175E7200B2526E /* HotKey */,
|
|
||||||
);
|
);
|
||||||
productName = phpmon;
|
productName = phpmon;
|
||||||
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
|
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
|
||||||
@ -788,7 +876,6 @@
|
|||||||
);
|
);
|
||||||
name = "phpmon-tests";
|
name = "phpmon-tests";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
C4C1019527C659B7001FACC2 /* HotKey */,
|
|
||||||
);
|
);
|
||||||
productName = "phpmon-tests";
|
productName = "phpmon-tests";
|
||||||
productReference = C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */;
|
productReference = C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */;
|
||||||
@ -822,7 +909,6 @@
|
|||||||
);
|
);
|
||||||
mainGroup = C41C1B2A22B0097F00E7CF16;
|
mainGroup = C41C1B2A22B0097F00E7CF16;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
|
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -863,12 +949,14 @@
|
|||||||
files = (
|
files = (
|
||||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||||
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||||
|
C42CFB1827DFDFDC00862737 /* nicoverbruggen_isolated.test in Resources */,
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
||||||
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */,
|
C43A8A2025D9D1D700591B77 /* brew.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 */,
|
||||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
||||||
|
C42CFB1627DFDE7900862737 /* nicoverbruggen.test in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -903,13 +991,18 @@
|
|||||||
C48D0CA325CC992000CC7490 /* StatsView.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 */,
|
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
||||||
|
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
||||||
|
C44067F527E2582B0045BD4E /* SiteListNameCell.swift in Sources */,
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */,
|
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */,
|
||||||
|
C44067F727E258410045BD4E /* SiteListPhpCell.swift in Sources */,
|
||||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
||||||
|
C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */,
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
||||||
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */,
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */,
|
||||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||||
@ -918,9 +1011,11 @@
|
|||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.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 */,
|
||||||
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 */,
|
||||||
|
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 */,
|
||||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||||
@ -936,6 +1031,7 @@
|
|||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
||||||
|
C44067FB27E25FD70045BD4E /* SiteListTLSCell.swift in Sources */,
|
||||||
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */,
|
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
||||||
@ -947,17 +1043,23 @@
|
|||||||
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 */,
|
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
||||||
|
C4D5CFCA27E0F9CD00035329 /* NginxConfigParser.swift in Sources */,
|
||||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.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 */,
|
||||||
|
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||||
|
C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */,
|
||||||
|
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||||
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 */,
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
||||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.swift in Sources */,
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
|
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
|
||||||
@ -968,9 +1070,11 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
C449B4F427EE7FC800C47E8A /* SiteListKindCell.swift in Sources */,
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||||
|
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||||
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
|
||||||
@ -978,18 +1082,23 @@
|
|||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
|
C41C02AA27E61CA3009F26CB /* SiteScanner.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 */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.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 */,
|
||||||
|
C449B4F027EE7FB800C47E8A /* SiteListTLSCell.swift in Sources */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.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 */,
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
|
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
@ -999,10 +1108,12 @@
|
|||||||
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E819276F54D8003AC782 /* App+ConfigWatch.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 */,
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
|
C4D936CB27E3EE4A00BD69FE /* SiteListCellProtocol.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 */,
|
||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||||
@ -1010,7 +1121,6 @@
|
|||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
||||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
||||||
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */,
|
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
@ -1018,10 +1128,13 @@
|
|||||||
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 */,
|
||||||
|
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 */,
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
||||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
|
C449B4F127EE7FC200C47E8A /* SiteListNameCell.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 /* PrefsWC.swift in Sources */,
|
||||||
@ -1031,6 +1144,8 @@
|
|||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
||||||
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
|
C449B4F227EE7FC400C47E8A /* SiteListPhpCell.swift in Sources */,
|
||||||
|
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */,
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||||
@ -1043,7 +1158,9 @@
|
|||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
C4927F0C27B2DFC200C55AFD /* Errors.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 /* SiteListTypeCell.swift in Sources */,
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
||||||
@ -1207,7 +1324,7 @@
|
|||||||
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 = 717;
|
CURRENT_PROJECT_VERSION = 756;
|
||||||
DEBUG = YES;
|
DEBUG = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@ -1217,7 +1334,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.1.1;
|
MARKETING_VERSION = 5.2;
|
||||||
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 = "";
|
||||||
@ -1233,7 +1350,7 @@
|
|||||||
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 = 717;
|
CURRENT_PROJECT_VERSION = 756;
|
||||||
DEBUG = NO;
|
DEBUG = NO;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@ -1243,7 +1360,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.1.1;
|
MARKETING_VERSION = 5.2;
|
||||||
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 = "";
|
||||||
@ -1320,30 +1437,6 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
|
||||||
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/soffes/HotKey";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMinorVersion;
|
|
||||||
minimumVersion = 0.1.3;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
|
||||||
C4998F0526175E7200B2526E /* HotKey */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */;
|
|
||||||
productName = HotKey;
|
|
||||||
};
|
|
||||||
C4C1019527C659B7001FACC2 /* HotKey */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */;
|
|
||||||
productName = HotKey;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
|
||||||
};
|
};
|
||||||
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
|
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,13 @@
|
|||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "PHPMON_MARKETING_MODE"
|
||||||
|
value = "YES"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
66
README.md
@ -1,17 +1,12 @@
|
|||||||
> 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.
|
> 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!** ⭐️
|
||||||
> You can also send me [feedback](https://twitter.com/nicoverbruggen) if the app came in handy.<br>**Thank you!** ⭐️
|
|
||||||
|
|
||||||
<h1 align="center"><b>PHP Monitor</b> (phpmon)</h1>
|
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
||||||
|
|
||||||
<p align="center">
|
**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="./phpmon/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png" alt="phpmon icon" width="128px" />
|
|
||||||
</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</u>.
|
<img src="./docs/screenshot.jpg" width="1085px" alt="phpmon screenshot (menu bar app)"/>
|
||||||
|
|
||||||
<img src="./docs/screenshot50.jpg" width="1085px" 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. You can also add new domains as links, manage various services, and perform First Aid to fix all kinds of common PHP link issues.</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)!
|
||||||
|
|
||||||
@ -19,16 +14,19 @@ It's super convenient to switch between different versions of PHP. You'll even g
|
|||||||
|
|
||||||
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).
|
||||||
|
|
||||||
|
You can also add new domains as links, isolate sites, manage various services, and perform First Aid to fix all kinds of common PHP link issues.
|
||||||
|
|
||||||
## 🖥 System requirements
|
## 🖥 System requirements
|
||||||
|
|
||||||
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)
|
||||||
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
||||||
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
||||||
* The brew formula `php` has to be installed (which version is detected)
|
* Homebrew `php` formula is installed
|
||||||
* Laravel Valet 2.16.2 or higher (older versions might be compatible but are not supported)
|
* Laravel Valet 2.16 or newer (supports Valet 3)
|
||||||
|
|
||||||
_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`._
|
_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._
|
||||||
|
|
||||||
## 🚀 How to install
|
## 🚀 How to install
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ If you're still having issues, here's a few common questions & answers, as well
|
|||||||
<summary><strong>Which versions of PHP are supported?</strong></summary>
|
<summary><strong>Which versions of PHP are supported?</strong></summary>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>PHP 5.6</li>
|
<li>PHP 5.6 (only if you are running Valet 2)</li>
|
||||||
<li>PHP 7.0</li>
|
<li>PHP 7.0</li>
|
||||||
<li>PHP 7.1</li>
|
<li>PHP 7.1</li>
|
||||||
<li>PHP 7.2</li>
|
<li>PHP 7.2</li>
|
||||||
@ -90,7 +88,7 @@ If you're still having issues, here's a few common questions & answers, as well
|
|||||||
<li>PHP 8.2 (experimental)</li>
|
<li>PHP 8.2 (experimental)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Constants.swift#L16) file to see which versions are supported.
|
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Common/Core/Constants.swift#L16) file to see which versions are supported.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -151,6 +149,29 @@ This should install `dnsmasq` and set up Valet. Great, almost there!
|
|||||||
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>I have PHP Monitor installed, and it works. I want to upgrade my PHP installations to the latest version, what's the best way to do this?</strong></summary>
|
||||||
|
|
||||||
|
It's easy to make a mistake here, and end up with an unlinked version of PHP or have versions missing from PHP Monitor.
|
||||||
|
|
||||||
|
Here's what I usually do:
|
||||||
|
|
||||||
|
* Open PHP Monitor and select **First Aid & Services** > **Restore Homebrew Permissions**.
|
||||||
|
* Close PHP Monitor after the pop-up tells you the permissions were restored.
|
||||||
|
* Run `brew update-reset`
|
||||||
|
* Run `brew upgrade`
|
||||||
|
|
||||||
|
If after this, any PHP versions are missing in PHP Monitor, please run the following for the versions that are missing:
|
||||||
|
|
||||||
|
* Run `brew uninstall php@x.x` (where `x.x` is the version)
|
||||||
|
* Run `brew cleanup` (if you get any permission issues you may need to manually clean up the folder)
|
||||||
|
* Run `brew install php@x.x` (where `x.x` is the version)
|
||||||
|
|
||||||
|
You may still need to run `brew link php` after upgrading, too.
|
||||||
|
|
||||||
|
That's it. Now start up PHP Monitor again and you should be golden!
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>PHP Monitor tells me `php` is not installed...</strong></summary>
|
<summary><strong>PHP Monitor tells me `php` is not installed...</strong></summary>
|
||||||
|
|
||||||
@ -204,6 +225,12 @@ Usually this is a duplicate extension declaration causing issues, or an extensio
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>The option to isolate a site is disabled! What's going on?</strong></summary>
|
||||||
|
|
||||||
|
Make sure you have at least **Valet 3.0** installed, since support for isolation was added in this version of Valet. (Please note that this version of Valet drops support for PHP 5.6.)
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>One of the limits (memory limit, max POST size, max upload size) shows an exclamation mark!</strong></summary>
|
<summary><strong>One of the limits (memory limit, max POST size, max upload size) shows an exclamation mark!</strong></summary>
|
||||||
|
|
||||||
@ -366,13 +393,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, I have been lucky enough to do various experiments during work hours at [DIVE](https://dive.be). I'd also like to shout out the following folks:
|
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:
|
||||||
|
|
||||||
* My colleagues at [DIVE](https://dive.be)
|
* My colleagues at [DIVE](https://dive.be)
|
||||||
* 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 (appreciate it!)
|
||||||
* Everyone in the Laravel community who shared the app (thanks!)
|
* Everyone in the Laravel community who shared the app (thanks!)
|
||||||
* Everyone who left feedback via issues & who donated to keep the project up and running
|
|
||||||
|
|
||||||
Thank you very much for your contributions, kind words and support.
|
Thank you very much for your contributions, kind words and support.
|
||||||
|
|
||||||
@ -388,7 +416,9 @@ In order to save power, this only happens once every 60 seconds.
|
|||||||
|
|
||||||
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
|
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
|
||||||
|
|
||||||
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be much faster than Valet’s switcher.
|
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be a bit faster than Valet’s switcher.
|
||||||
|
|
||||||
|
If you're using Valet 3, versions of PHP-FPM required to keep isolated sites up and running will also be started or stopped as needed.
|
||||||
|
|
||||||
### Config change detection
|
### Config change detection
|
||||||
|
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
||||||
|
|
||||||
| 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 | Recommended Valet Version |
|
||||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||||
| 5 | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
| 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) |
|
||||||
|
|
||||||
|
_(*) 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._
|
||||||
|
|
||||||
## Legacy versions
|
## Legacy versions
|
||||||
|
|
||||||
|
BIN
docs/logo.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshot.jpg
Normal file
After Width: | Height: | Size: 345 KiB |
Before Width: | Height: | Size: 370 KiB |
32
phpmon-tests/Parsers/NginxConfigParserTest.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// NginxConfigParserTest.swift
|
||||||
|
// phpmon-tests
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 29/11/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class NginxConfigParserTest: XCTestCase {
|
||||||
|
|
||||||
|
static var regularUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nicoverbruggen", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var isolatedUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nicoverbruggen_isolated", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanDetermineIsolation() throws {
|
||||||
|
XCTAssertNil(
|
||||||
|
NginxConfigParser(filePath: NginxConfigParserTest.regularUrl.path).isolatedVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
"8.1",
|
||||||
|
NginxConfigParser(filePath: NginxConfigParserTest.isolatedUrl.path).isolatedVersion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,6 +32,7 @@ class ValetConfigParserTest: XCTestCase {
|
|||||||
"/Users/username/.config/valet/Sites",
|
"/Users/username/.config/valet/Sites",
|
||||||
"/Users/username/Sites"
|
"/Users/username/Sites"
|
||||||
])
|
])
|
||||||
|
XCTAssertEqual(config.defaultSite, "/Users/username/default-site")
|
||||||
XCTAssertEqual(config.loopback, "127.0.0.1")
|
XCTAssertEqual(config.loopback, "127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
phpmon-tests/Test Files/nicoverbruggen.test
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:443 ssl http2;
|
||||||
|
#listen 127.0.0.1:443 ssl http2; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 512M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.crt";
|
||||||
|
ssl_certificate_key "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.key";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:60;
|
||||||
|
#listen 127.0.0.1:60; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
|
||||||
|
add_header X-Robots-Tag 'noindex, nofollow, nosnippet, noarchive';
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
94
phpmon-tests/Test Files/nicoverbruggen_isolated.test
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:443 ssl http2;
|
||||||
|
#listen 127.0.0.1:443 ssl http2; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 512M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.crt";
|
||||||
|
ssl_certificate_key "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.key";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
# ISOLATED_PHP_VERSION=php@8.1
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet81.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:60;
|
||||||
|
#listen 127.0.0.1:60; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
|
||||||
|
add_header X-Robots-Tag 'noindex, nofollow, nosnippet, noarchive';
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,5 +4,6 @@
|
|||||||
"/Users/username/.config/valet/Sites",
|
"/Users/username/.config/valet/Sites",
|
||||||
"/Users/username/Sites"
|
"/Users/username/Sites"
|
||||||
],
|
],
|
||||||
"loopback": "127.0.0.1"
|
"loopback": "127.0.0.1",
|
||||||
|
"default": "/Users/username/default-site"
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ class PhpVersionDetectionTest: XCTestCase {
|
|||||||
"unrelatedphp@1.0", // should be omitted, invalid
|
"unrelatedphp@1.0", // should be omitted, invalid
|
||||||
"php@5.6",
|
"php@5.6",
|
||||||
"php@5.4" // should be omitted, not supported
|
"php@5.4" // should be omitted, not supported
|
||||||
], checkBinaries: false)
|
], checkBinaries: false, generateHelpers: false)
|
||||||
|
|
||||||
XCTAssertEqual(outcome, ["8.0", "7.0", "5.6"])
|
XCTAssertEqual(outcome, ["8.0", "7.0"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ class ValetVersionExtractorTest: XCTestCase {
|
|||||||
|
|
||||||
func testDetermineValetVersion() {
|
func testDetermineValetVersion() {
|
||||||
let version = valet("--version", sudo: false)
|
let version = valet("--version", sudo: false)
|
||||||
XCTAssert(version.contains("Laravel Valet 2."))
|
XCTAssert(version.contains("Laravel Valet 2") || version.contains("Laravel Valet 3"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
25
phpmon/Assets.xcassets/IconDefault.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Default.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Default@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
phpmon/Assets.xcassets/IconDefault.imageset/Default.png
vendored
Normal file
After Width: | Height: | Size: 861 B |
BIN
phpmon/Assets.xcassets/IconDefault.imageset/Default@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
25
phpmon/Assets.xcassets/Isolated.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Isolated.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Isolated@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
phpmon/Assets.xcassets/Isolated.imageset/Isolated.png
vendored
Normal file
After Width: | Height: | Size: 690 B |
BIN
phpmon/Assets.xcassets/Isolated.imageset/Isolated@2x.png
vendored
Normal file
After Width: | Height: | Size: 827 B |
@ -41,8 +41,8 @@ class Actions {
|
|||||||
"\(Paths.brew) services stop dnsmasq",
|
"\(Paths.brew) services stop dnsmasq",
|
||||||
]
|
]
|
||||||
var cellarCommands = [
|
var cellarCommands = [
|
||||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/nginx",
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/nginx",
|
||||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/dnsmasq"
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/dnsmasq"
|
||||||
]
|
]
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { version in
|
PhpEnv.shared.availablePhpVersions.forEach { version in
|
||||||
@ -50,7 +50,7 @@ class Actions {
|
|||||||
? "php"
|
? "php"
|
||||||
: "php@\(version)"
|
: "php@\(version)"
|
||||||
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||||
cellarCommands.append("chown -R \(Paths.whoami):staff \(Paths.cellarPath)/\(formula)")
|
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let script =
|
let script =
|
||||||
@ -124,25 +124,13 @@ class Actions {
|
|||||||
If this does not solve the issue, the user may need to install additional
|
If this does not solve the issue, the user may need to install additional
|
||||||
extensions and/or run `composer global update`.
|
extensions and/or run `composer global update`.
|
||||||
*/
|
*/
|
||||||
public static func fixMyValet()
|
public static func fixMyValet(completed: @escaping () -> Void)
|
||||||
{
|
{
|
||||||
brew("services restart dnsmasq", sudo: true)
|
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpVersion, completion: {
|
||||||
|
brew("services restart dnsmasq", sudo: true)
|
||||||
PhpEnv.shared.detectPhpVersions().forEach { (version) in
|
brew("services restart php", sudo: true)
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
brew("services restart nginx", sudo: true)
|
||||||
brew("unlink php@\(version)")
|
completed()
|
||||||
brew("services stop \(formula)")
|
})
|
||||||
brew("services stop \(formula)", sudo: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
brew("services stop dnsmasq")
|
|
||||||
brew("services stop php")
|
|
||||||
brew("services stop nginx")
|
|
||||||
|
|
||||||
brew("link php --overwrite --force")
|
|
||||||
|
|
||||||
brew("services restart dnsmasq", sudo: true)
|
|
||||||
brew("services restart php", sudo: true)
|
|
||||||
brew("services restart nginx", sudo: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ struct Constants {
|
|||||||
// STABLE RELEASES
|
// STABLE RELEASES
|
||||||
// ====================
|
// ====================
|
||||||
// Versions of PHP that are stable and are supported.
|
// Versions of PHP that are stable and are supported.
|
||||||
"5.6",
|
"5.6", // only supported when Valet 2.x is active
|
||||||
"7.0",
|
"7.0",
|
||||||
"7.1",
|
"7.1",
|
||||||
"7.2",
|
"7.2",
|
||||||
|
@ -20,16 +20,19 @@ struct HomebrewService: Decodable, Equatable {
|
|||||||
let error_log_path: String?
|
let error_log_path: String?
|
||||||
|
|
||||||
public static func loadAll(
|
public static func loadAll(
|
||||||
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"]
|
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"],
|
||||||
) async -> [HomebrewService] {
|
completion: @escaping ([HomebrewService]) -> Void
|
||||||
return try! JSONDecoder().decode(
|
) {
|
||||||
[HomebrewService].self,
|
DispatchQueue.global(qos: .background).async {
|
||||||
from: Shell.pipe(
|
let data = Shell
|
||||||
"sudo \(Paths.brew) services info --all --json",
|
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
||||||
requiresPath: true
|
.data(using: .utf8)!
|
||||||
).data(using: .utf8)!
|
|
||||||
).filter({ service in
|
let services = try! JSONDecoder()
|
||||||
return filter.contains(service.name)
|
.decode([HomebrewService].self, from: data)
|
||||||
})
|
.filter({ return filter.contains($0.name) })
|
||||||
|
|
||||||
|
completion(services)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,13 +117,23 @@ class PhpEnv {
|
|||||||
/**
|
/**
|
||||||
Extracts valid PHP versions from an array of strings.
|
Extracts valid PHP versions from an array of strings.
|
||||||
This array of strings is usually retrieved from `grep`.
|
This array of strings is usually retrieved from `grep`.
|
||||||
|
|
||||||
|
If `generateHelpers` is set to true, after detecting
|
||||||
|
all versions, helper scripts are generated as well.
|
||||||
*/
|
*/
|
||||||
public func extractPhpVersions(
|
public func extractPhpVersions(
|
||||||
from versions: [String],
|
from versions: [String],
|
||||||
checkBinaries: Bool = true
|
checkBinaries: Bool = true,
|
||||||
|
generateHelpers: Bool = true
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
var output : [String] = []
|
var output : [String] = []
|
||||||
|
|
||||||
|
var supported = Constants.SupportedPhpVersions
|
||||||
|
|
||||||
|
if !Valet.enabled(feature: .supportForPhp56) {
|
||||||
|
supported.removeAll { $0 == "5.6" }
|
||||||
|
}
|
||||||
|
|
||||||
versions.filter { (version) -> Bool in
|
versions.filter { (version) -> Bool in
|
||||||
// Omit everything that doesn't start with php@
|
// Omit everything that doesn't start with php@
|
||||||
// (e.g. something-php@8.0 won't be detected)
|
// (e.g. something-php@8.0 won't be detected)
|
||||||
@ -133,13 +143,17 @@ class PhpEnv {
|
|||||||
// Only append the version if it doesn't already exist (avoid dupes),
|
// Only append the version if it doesn't already exist (avoid dupes),
|
||||||
// is supported and where the binary exists (avoids broken installs)
|
// is supported and where the binary exists (avoids broken installs)
|
||||||
if !output.contains(version)
|
if !output.contains(version)
|
||||||
&& Constants.SupportedPhpVersions.contains(version)
|
&& supported.contains(version)
|
||||||
&& (checkBinaries ? Filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
&& (checkBinaries ? Filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
||||||
{
|
{
|
||||||
output.append(version)
|
output.append(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if generateHelpers {
|
||||||
|
output.forEach { PhpHelper.generate(for: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
phpmon/Common/PHP/PHP Version/PhpHelper.swift
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// PhpHelper.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 17/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpHelper {
|
||||||
|
|
||||||
|
static let keyPhrase = "This file was automatically generated by PHP Monitor."
|
||||||
|
|
||||||
|
public static func generate(for version: String) {
|
||||||
|
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||||
|
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
|
do {
|
||||||
|
let destination = "/usr/local/bin/pm\(dotless)"
|
||||||
|
if FileManager.default.fileExists(atPath: destination) {
|
||||||
|
let contents = try String(contentsOfFile: destination)
|
||||||
|
if !contents.contains(keyPhrase) {
|
||||||
|
Log.info("The file at '\(destination)' already exists and was not generated by PHP Monitor (or is unreadable). Not updating this file.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's follow the symlink to the PHP binary folder
|
||||||
|
let path = URL(fileURLWithPath: "\(Paths.optPath)/php@\(version)/bin")
|
||||||
|
.resolvingSymlinksInPath().path
|
||||||
|
|
||||||
|
// The contents of the script!
|
||||||
|
let script = """
|
||||||
|
#!/bin/zsh
|
||||||
|
# \(keyPhrase)
|
||||||
|
# It reflects the location of PHP \(version)'s binaries on your system.
|
||||||
|
# Usage: . pm\(dotless)
|
||||||
|
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] \\
|
||||||
|
&& echo "PHP Monitor has enabled this terminal to use PHP \(version)." \\
|
||||||
|
|| echo "You must run '. pm\(dotless)' (or 'source pm\(dotless)') instead!";
|
||||||
|
export PATH=\(path):$PATH
|
||||||
|
"""
|
||||||
|
|
||||||
|
// Write to the destination
|
||||||
|
try script.write(
|
||||||
|
to: URL(fileURLWithPath: destination),
|
||||||
|
atomically: true,
|
||||||
|
encoding: String.Encoding.utf8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Make sure the file is executable
|
||||||
|
Shell.run("chmod +x \(destination)")
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
class PhpInstallation {
|
class PhpInstallation {
|
||||||
|
|
||||||
var longVersion: PhpVersionNumber
|
var versionNumber: PhpVersionNumber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
||||||
@ -19,7 +19,7 @@ class PhpInstallation {
|
|||||||
init(_ version: String) {
|
init(_ version: String) {
|
||||||
|
|
||||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||||
self.longVersion = PhpVersionNumber.make(from: version)!
|
self.versionNumber = PhpVersionNumber.make(from: version)!
|
||||||
|
|
||||||
if Filesystem.fileExists(phpConfigExecutablePath) {
|
if Filesystem.fileExists(phpConfigExecutablePath) {
|
||||||
let longVersionString = Command.execute(
|
let longVersionString = Command.execute(
|
||||||
@ -29,7 +29,7 @@ class PhpInstallation {
|
|||||||
|
|
||||||
// The parser should always work, or the string has to be very unusual.
|
// The parser should always work, or the string has to be very unusual.
|
||||||
// If so, the app SHOULD crash, so that the users report what's up.
|
// If so, the app SHOULD crash, so that the users report what's up.
|
||||||
self.longVersion = try! PhpVersionNumber.parse(longVersionString)
|
self.versionNumber = try! PhpVersionNumber.parse(longVersionString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,20 +24,26 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
{
|
{
|
||||||
Log.info("Switching to \(version), unlinking all versions...")
|
Log.info("Switching to \(version), unlinking all versions...")
|
||||||
|
|
||||||
|
let isolated = Valet.shared.sites.filter { site in
|
||||||
|
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()
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
let formula = (available == PhpEnv.brewPhpVersion)
|
self.disableDefaultPhpFpmPool(available)
|
||||||
? "php" : "php@\(available)"
|
self.stopPhpVersion(available)
|
||||||
|
|
||||||
brew("unlink \(formula)")
|
|
||||||
brew("services stop \(formula)", sudo: true)
|
|
||||||
|
|
||||||
Log.perf("Unlinked and stopped services for \(formula)")
|
|
||||||
|
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,16 +52,58 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
Log.info("All versions have been unlinked!")
|
Log.info("All versions have been unlinked!")
|
||||||
Log.info("Linking the new version!")
|
Log.info("Linking the new version!")
|
||||||
|
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
for formula in versions {
|
||||||
brew("link \(formula) --overwrite --force")
|
self.startPhpVersion(formula, primary: (version == formula))
|
||||||
brew("services start \(formula)", sudo: true)
|
}
|
||||||
|
|
||||||
Log.info("Restarting nginx, just to be sure!")
|
Log.info("Restarting nginx, just to be sure!")
|
||||||
brew("services restart nginx", sudo: true)
|
brew("services restart nginx", sudo: true)
|
||||||
|
|
||||||
Log.info("The new version has been linked!")
|
Log.info("The new version(s) have been linked!")
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func disableDefaultPhpFpmPool(_ version: String) {
|
||||||
|
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||||
|
if FileManager.default.fileExists(atPath: pool) {
|
||||||
|
Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).")
|
||||||
|
let existing = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf")!
|
||||||
|
let new = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf.disabled-by-phpmon")!
|
||||||
|
do {
|
||||||
|
try FileManager.default.moveItem(at: existing, to: new)
|
||||||
|
Log.info("Success: A default `www.conf` file was disabled for PHP \(version).")
|
||||||
|
} catch {
|
||||||
|
Log.err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopPhpVersion(_ version: String) {
|
||||||
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
brew("unlink \(formula)")
|
||||||
|
brew("services stop \(formula)", sudo: true)
|
||||||
|
Log.info("Unlinked and stopped services for \(formula)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startPhpVersion(_ version: String, primary: Bool) {
|
||||||
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
|
||||||
|
if (primary) {
|
||||||
|
Log.info("\(formula) is the primary formula, linking and starting services...")
|
||||||
|
brew("link \(formula) --overwrite --force")
|
||||||
|
} else {
|
||||||
|
Log.info("\(formula) is an isolated PHP version, starting services only...")
|
||||||
|
}
|
||||||
|
|
||||||
|
brew("services start \(formula)", sudo: true)
|
||||||
|
|
||||||
|
if Valet.enabled(feature: .isolatedSites) && primary {
|
||||||
|
let socketVersion = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
Shell.run("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock")
|
||||||
|
Log.info("Symlinked new socket version (valet\(socketVersion).sock → valet.sock).")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import HotKey
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension App {
|
extension App {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?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"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
<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"/>
|
||||||
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
|
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
|
||||||
@ -387,10 +387,10 @@
|
|||||||
<window key="window" title="Domains" subtitle="Linked & Parked" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
<window key="window" title="Domains" subtitle="Linked & Parked" 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"/>
|
||||||
<rect key="contentRect" x="425" y="461" width="550" height="263"/>
|
<rect key="contentRect" x="425" y="461" width="600" height="263"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
<view key="contentView" id="uVx-Da-x4I">
|
<view key="contentView" id="uVx-Da-x4I">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="550" height="263"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="263"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="594015E3-8428-4926-9341-4B8CE4C7E373" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="OOz-oZ-vlN">
|
<toolbar key="toolbar" implicitIdentifier="594015E3-8428-4926-9341-4B8CE4C7E373" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="OOz-oZ-vlN">
|
||||||
@ -407,12 +407,12 @@
|
|||||||
<action selector="pressedReload:" target="8Ec-9q-82s" id="fLc-bD-oYQ"/>
|
<action selector="pressedReload:" target="8Ec-9q-82s" id="fLc-bD-oYQ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
<searchToolbarItem implicitItemIdentifier="629F0782-3C5F-4CD0-9396-3A054A422180" label="Search" paletteLabel="Search" visibilityPriority="1001" id="Q7Z-fw-lB9">
|
<searchToolbarItem implicitItemIdentifier="7C834FBE-7118-4082-A09F-7CBECEC1356A" label="Search" paletteLabel="Search" visibilityPriority="1001" id="G2g-jS-RVc">
|
||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<searchField key="view" verticalHuggingPriority="750" textCompletion="NO" id="oWA-TH-Pm7">
|
<searchField key="view" verticalHuggingPriority="750" textCompletion="NO" id="0gE-Yr-MLy">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="100" height="21"/>
|
<rect key="frame" x="0.0" y="0.0" width="100" height="21"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsSearchStringImmediately="YES" id="3NO-6x-aLc">
|
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsSearchStringImmediately="YES" id="vp9-vH-goQ">
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -423,7 +423,7 @@
|
|||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="GsC-ra-40U"/>
|
<toolbarItem reference="GsC-ra-40U"/>
|
||||||
<toolbarItem reference="YtK-vM-5y7"/>
|
<toolbarItem reference="YtK-vM-5y7"/>
|
||||||
<searchToolbarItem reference="Q7Z-fw-lB9"/>
|
<searchToolbarItem reference="G2g-jS-RVc"/>
|
||||||
</defaultToolbarItems>
|
</defaultToolbarItems>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
<connections>
|
<connections>
|
||||||
@ -431,13 +431,13 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="searchToolbarItem" destination="Q7Z-fw-lB9" id="J5o-oh-VhO"/>
|
<outlet property="searchToolbarItem" destination="G2g-jS-RVc" id="xlc-qe-k7e"/>
|
||||||
<segue destination="JZI-Vd-9oq" kind="relationship" relationship="window.shadowedContentViewController" id="9Gy-Gw-hPH"/>
|
<segue destination="JZI-Vd-9oq" kind="relationship" relationship="window.shadowedContentViewController" id="9Gy-Gw-hPH"/>
|
||||||
</connections>
|
</connections>
|
||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="VCP-dF-cqM" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="VCP-dF-cqM" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-374" y="758"/>
|
<point key="canvasLocation" x="-374" y="746"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="HTI-x5-rOp">
|
<scene sceneID="HTI-x5-rOp">
|
||||||
@ -532,7 +532,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="81" height="32"/>
|
<rect key="frame" x="13" y="13" width="82" 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 +651,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="364" y="13" width="103" height="32"/>
|
<rect key="frame" x="363" y="13" width="104" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="Create Link" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WwW-Wv-I8s">
|
<buttonCell key="cell" type="push" title="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"/>
|
||||||
@ -720,7 +720,7 @@ Gw
|
|||||||
<rect key="frame" x="20" y="185" width="440" height="22"/>
|
<rect key="frame" x="20" y="185" width="440" height="22"/>
|
||||||
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="m8d-XF-kh9">
|
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="m8d-XF-kh9">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<url key="url" string="file:///Users/nicoverbruggen/Code/nicoverbruggen.be/"/>
|
<url key="url" string="file:///Users/"/>
|
||||||
</pathCell>
|
</pathCell>
|
||||||
</pathControl>
|
</pathControl>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P0B-Ht-R8n">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P0B-Ht-R8n">
|
||||||
@ -732,7 +732,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="230" y="23" width="128" height="14"/>
|
<rect key="frame" x="229" y="23" width="128" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="That link already exists." id="jOt-n6-TQf">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="That link 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"/>
|
||||||
@ -788,35 +788,68 @@ Gw
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="6XV-bG-0N1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="6XV-bG-0N1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="191" y="1099"/>
|
<point key="canvasLocation" x="191" y="1098.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Site ListVC-->
|
<!--Site ListVC-->
|
||||||
<scene sceneID="aZt-6w-TFl">
|
<scene sceneID="aZt-6w-TFl">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController identifier="siteList" storyboardIdentifier="siteList" id="JZI-Vd-9oq" customClass="SiteListVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController identifier="siteList" storyboardIdentifier="siteList" id="JZI-Vd-9oq" customClass="SiteListVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="rIZ-4U-bhj">
|
<view key="view" id="rIZ-4U-bhj">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<customView id="j65-Lf-0lG">
|
<scrollView borderType="none" horizontalLineScroll="54" horizontalPageScroll="10" verticalLineScroll="54" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p0j-eB-I2i">
|
||||||
<rect key="frame" x="9" y="0.0" width="581" height="203"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="6IL-DW-37w">
|
||||||
</customView>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="54" horizontalPageScroll="10" verticalLineScroll="54" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p0j-eB-I2i">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="309"/>
|
|
||||||
<clipView key="contentView" id="6IL-DW-37w">
|
|
||||||
<rect key="frame" x="1" y="1" width="598" height="307"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" 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="598" height="307"/>
|
<rect key="frame" x="0.0" y="0.0" width="662" height="281"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||||
|
<color key="gridColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<tableColumns>
|
<tableColumns>
|
||||||
<tableColumn width="586" minWidth="40" maxWidth="10000" id="oeH-B2-0rA">
|
<tableColumn identifier="TLS" width="36" minWidth="36" maxWidth="36" id="z6X-Ni-Eev">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="TLS">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="OsS-YW-O4s">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Secure"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListTLSCell" id="hft-M4-nWb" customClass="SiteListTLSCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="18" y="0.0" width="34" height="55"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Wel-Ho-Kpp">
|
||||||
|
<rect key="frame" x="7" y="18" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="7mC-me-Nse"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="AjD-xX-suI"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Lock" id="gK0-Mh-K9Y"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Wel-Ho-Kpp" firstAttribute="centerY" secondItem="hft-M4-nWb" secondAttribute="centerY" id="L6B-iA-BCQ"/>
|
||||||
|
<constraint firstItem="Wel-Ho-Kpp" firstAttribute="centerX" secondItem="hft-M4-nWb" secondAttribute="centerX" id="jAF-AV-EeX"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageViewLock" destination="Wel-Ho-Kpp" id="iji-uw-8we"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="DOMAIN" width="290" minWidth="250" maxWidth="10000" id="oeH-B2-0rA">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Domain">
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
</tableHeaderCell>
|
</tableHeaderCell>
|
||||||
@ -825,14 +858,15 @@ Gw
|
|||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Domain"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteItem" wantsLayer="YES" id="5GY-nN-BWd" customClass="SiteListCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="siteListNameCell" wantsLayer="YES" id="5GY-nN-BWd" customClass="SiteListNameCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="8" y="0.0" width="581" height="54"/>
|
<rect key="frame" x="69" y="0.0" width="290" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="XJL-Uw-frD">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="XJL-Uw-frD">
|
||||||
<rect key="frame" x="38" y="26" width="145" height="16"/>
|
<rect key="frame" x="3" y="26" width="145" height="16"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="my-domain-name.test" id="SGC-Gm-Mxd">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="my-domain-name.test" id="SGC-Gm-Mxd">
|
||||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -840,106 +874,166 @@ Gw
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="CXK-Q9-CpO">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="CXK-Q9-CpO">
|
||||||
<rect key="frame" x="38" y="12" width="75" height="14"/>
|
<rect key="frame" x="3" y="12" width="75" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="~/path/to/site" id="fe7-Ha-mR9">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="~/path/to/site" id="fe7-Ha-mR9">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<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>
|
||||||
</textField>
|
</textField>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QPX-eu-eV8">
|
</subviews>
|
||||||
<rect key="frame" x="10" y="22" width="20" height="20"/>
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="XJL-Uw-frD" secondAttribute="trailing" constant="20" symbolic="YES" id="62d-gz-2lX"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="CXK-Q9-CpO" secondAttribute="trailing" constant="20" symbolic="YES" id="Agn-Ag-QYd"/>
|
||||||
|
<constraint firstItem="CXK-Q9-CpO" firstAttribute="leading" secondItem="XJL-Uw-frD" secondAttribute="leading" id="Ojw-VZ-3EG"/>
|
||||||
|
<constraint firstItem="XJL-Uw-frD" firstAttribute="top" secondItem="5GY-nN-BWd" secondAttribute="top" constant="12" id="QeE-c7-I9U"/>
|
||||||
|
<constraint firstItem="CXK-Q9-CpO" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="bottom" id="VKg-Vq-sYa"/>
|
||||||
|
<constraint firstItem="XJL-Uw-frD" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" constant="5" id="u1q-b0-iKq"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="labelPathName" destination="CXK-Q9-CpO" id="iVZ-cL-azB"/>
|
||||||
|
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="ENVIRONMENT" width="100" minWidth="100" maxWidth="150" id="hzb-XI-Out">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Active">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="ryK-6j-qWW">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="PHP"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListPhpCell" wantsLayer="YES" id="T49-0U-d58" customClass="SiteListPhpCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="376" y="0.0" width="100" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZXQ-bg-Xba">
|
||||||
|
<rect key="frame" x="27" y="18" width="70" height="18"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="20" id="Bmk-CN-Yyn"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="70" id="MBa-bB-DTB"/>
|
||||||
<constraint firstAttribute="height" constant="20" id="d4z-lb-Ww0"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Lock" id="aJ0-ia-YrZ"/>
|
<buttonCell key="cell" type="inline" title="PHP X.X" bezelStyle="inline" alignment="center" borderStyle="border" inset="2" id="Tfk-YR-L4B">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="smallSystemBold"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedPhpVersion:" target="T49-0U-d58" id="jVO-TS-F6d"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Yq0-qk-bFt">
|
||||||
|
<rect key="frame" x="1" y="18" width="18" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="18" id="5fd-EQ-BgV"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="nP7-13-SSn"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="S66-ow-Jo1">
|
||||||
|
<imageReference key="image" image="Checkmark" symbolScale="default"/>
|
||||||
|
</imageCell>
|
||||||
|
<color key="contentTintColor" name="IconColorGreen"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jKi-Ls-7FZ">
|
</subviews>
|
||||||
<rect key="frame" x="474" y="28" width="64" height="11"/>
|
<constraints>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="DRIVER TYPE" id="fjd-eb-itv">
|
<constraint firstItem="Yq0-qk-bFt" firstAttribute="centerY" secondItem="T49-0U-d58" secondAttribute="centerY" id="4Kz-lX-kOP"/>
|
||||||
<font key="font" metaFont="miniSystem"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="leading" secondItem="Yq0-qk-bFt" secondAttribute="trailing" constant="8" symbolic="YES" id="6jM-Qf-SG3"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="centerY" secondItem="T49-0U-d58" secondAttribute="centerY" id="FFK-4B-qIb"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="centerX" secondItem="T49-0U-d58" secondAttribute="centerX" constant="12" id="zjr-UQ-Dd7"/>
|
||||||
</textFieldCell>
|
</constraints>
|
||||||
</textField>
|
<connections>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TbX-e2-3QL">
|
<outlet property="buttonPhpVersion" destination="ZXQ-bg-Xba" id="vxL-if-CCC"/>
|
||||||
<rect key="frame" x="474" y="15" width="36" height="14"/>
|
<outlet property="imageViewPhpVersionOK" destination="Yq0-qk-bFt" id="0hT-cZ-9NI"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Driver" id="GMt-SG-vFl">
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="KIND" width="36" minWidth="36" maxWidth="36" id="7EV-ZL-92u">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Kind">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="kAe-u7-lN6">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Kind"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListKindCell" wantsLayer="YES" id="AhT-xR-16a" customClass="SiteListKindCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="493" y="0.0" width="36" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sYR-vb-OW1">
|
||||||
|
<rect key="frame" x="9" y="18" width="18" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="18" id="XcB-uw-szU"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="bGN-Vh-Sh0"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="IconLinked" id="T6e-IU-aZy"/>
|
||||||
|
<color key="contentTintColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="sYR-vb-OW1" firstAttribute="centerY" secondItem="AhT-xR-16a" secondAttribute="centerY" id="4mB-oS-T6I"/>
|
||||||
|
<constraint firstItem="sYR-vb-OW1" firstAttribute="centerX" secondItem="AhT-xR-16a" secondAttribute="centerX" id="LyQ-XZ-J3u"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageViewType" destination="sYR-vb-OW1" id="txH-es-roq"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="TYPE" width="100" minWidth="100" maxWidth="100" id="ncU-Ge-cyW">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Project Type">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="qHO-1M-TT2">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Type"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListTypeCell" wantsLayer="YES" id="ntU-Rl-ciP" customClass="SiteListTypeCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="546" y="0.0" width="97" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ljl-8B-key">
|
||||||
|
<rect key="frame" x="6" y="26" width="93" height="14"/>
|
||||||
|
<textFieldCell key="cell" alignment="left" title="Laravel" id="0lu-L6-oKr">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="syz-LF-l6P">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aPK-Xc-J4B">
|
||||||
<rect key="frame" x="0.0" y="-2" width="581" height="5"/>
|
<rect key="frame" x="6" y="15" width="93" height="11"/>
|
||||||
</box>
|
<textFieldCell key="cell" alignment="left" title="PHP 8.0" id="puf-Jh-ham">
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0NQ-ZD-CqD">
|
<font key="font" metaFont="miniSystem"/>
|
||||||
<rect key="frame" x="450" y="18" width="18" height="18"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<constraints>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
<constraint firstAttribute="width" constant="18" id="Suw-gm-AEi"/>
|
</textFieldCell>
|
||||||
<constraint firstAttribute="height" constant="18" id="qO6-vg-5nC"/>
|
</textField>
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="IconLinked" id="2ng-pK-kvv"/>
|
|
||||||
<color key="contentTintColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</imageView>
|
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3xt-wC-hUJ">
|
|
||||||
<rect key="frame" x="363" y="18" width="75" height="18"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="75" id="VI8-MP-7Hv"/>
|
|
||||||
</constraints>
|
|
||||||
<buttonCell key="cell" type="inline" title=" PHP X.X" bezelStyle="inline" alignment="center" borderStyle="border" inset="2" id="anZ-hP-G0R">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="smallSystemBold"/>
|
|
||||||
</buttonCell>
|
|
||||||
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="pressedPhpVersion:" target="5GY-nN-BWd" id="mB5-WD-aZy"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5aN-ZI-D7U">
|
|
||||||
<rect key="frame" x="341" y="20" width="14" height="14"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="14" id="NKD-Pc-okU"/>
|
|
||||||
<constraint firstAttribute="width" constant="14" id="wrl-lJ-3eN"/>
|
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Checkmark" id="R5o-Cd-a91"/>
|
|
||||||
<color key="contentTintColor" name="IconColorGreen"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" secondItem="3xt-wC-hUJ" secondAttribute="trailing" constant="12" id="2G8-Ow-FTu"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="top" secondItem="Ljl-8B-key" secondAttribute="bottom" id="0Ta-4x-l8E"/>
|
||||||
<constraint firstItem="3xt-wC-hUJ" firstAttribute="leading" secondItem="5aN-ZI-D7U" secondAttribute="trailing" constant="8" symbolic="YES" id="39Z-nB-kXx"/>
|
<constraint firstItem="Ljl-8B-key" firstAttribute="centerY" secondItem="ntU-Rl-ciP" secondAttribute="centerY" constant="-6" id="9Lh-QK-qnR"/>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TbX-e2-3QL" secondAttribute="trailing" constant="20" symbolic="YES" id="3vE-LR-S7N"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="leading" secondItem="Ljl-8B-key" secondAttribute="leading" id="PNb-yh-XOn"/>
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="leading" secondItem="0NQ-ZD-CqD" secondAttribute="trailing" constant="8" symbolic="YES" id="4cb-D9-8d1"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="trailing" secondItem="Ljl-8B-key" secondAttribute="trailing" id="T28-JT-Zkq"/>
|
||||||
<constraint firstItem="XJL-Uw-frD" firstAttribute="leading" secondItem="QPX-eu-eV8" secondAttribute="trailing" constant="10" id="55y-3V-RYt"/>
|
<constraint firstAttribute="trailing" secondItem="Ljl-8B-key" secondAttribute="trailing" id="Y7O-lc-fqb"/>
|
||||||
<constraint firstItem="syz-LF-l6P" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" id="8QK-nf-Fiw"/>
|
<constraint firstItem="Ljl-8B-key" firstAttribute="leading" secondItem="ntU-Rl-ciP" secondAttribute="leading" constant="8" id="idV-Vu-YeP"/>
|
||||||
<constraint firstItem="QPX-eu-eV8" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="top" id="9QB-jZ-k1V"/>
|
|
||||||
<constraint firstItem="QPX-eu-eV8" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" constant="10" id="GOj-sw-ZlZ"/>
|
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="top" secondItem="jKi-Ls-7FZ" secondAttribute="bottom" constant="-1" id="J29-wT-Uex"/>
|
|
||||||
<constraint firstItem="CXK-Q9-CpO" firstAttribute="leading" secondItem="XJL-Uw-frD" secondAttribute="leading" id="Ojw-VZ-3EG"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="syz-LF-l6P" secondAttribute="trailing" id="PWd-5k-AlD"/>
|
|
||||||
<constraint firstItem="XJL-Uw-frD" firstAttribute="top" secondItem="5GY-nN-BWd" secondAttribute="top" constant="12" id="QeE-c7-I9U"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" id="Utr-aa-tqX"/>
|
|
||||||
<constraint firstItem="CXK-Q9-CpO" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="bottom" id="VKg-Vq-sYa"/>
|
|
||||||
<constraint firstItem="5aN-ZI-D7U" firstAttribute="centerY" secondItem="3xt-wC-hUJ" secondAttribute="centerY" id="a6n-E2-i2x"/>
|
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" constant="5" id="cN8-zO-fnc"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="syz-LF-l6P" secondAttribute="bottom" id="gj7-cJ-Lle"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="CXK-Q9-CpO" secondAttribute="trailing" constant="8" symbolic="YES" id="iEd-Y3-zhp"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="XJL-Uw-frD" secondAttribute="trailing" constant="8" symbolic="YES" id="lLA-Jx-Q4W"/>
|
|
||||||
<constraint firstItem="3xt-wC-hUJ" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" id="vhb-WC-3NC"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="jKi-Ls-7FZ" secondAttribute="trailing" constant="45" id="vwD-Sg-Lzc"/>
|
|
||||||
<constraint firstItem="jKi-Ls-7FZ" firstAttribute="leading" secondItem="TbX-e2-3QL" secondAttribute="leading" id="zjN-s3-2Ww"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="buttonPhpVersion" destination="3xt-wC-hUJ" id="LpB-7n-qUr"/>
|
<outlet property="labelDriver" destination="Ljl-8B-key" id="82M-LT-pHT"/>
|
||||||
<outlet property="imageViewLock" destination="QPX-eu-eV8" id="Nnh-kB-adG"/>
|
<outlet property="labelPhpVersion" destination="aPK-Xc-J4B" id="TdR-xE-xhX"/>
|
||||||
<outlet property="imageViewPhpVersionOK" destination="5aN-ZI-D7U" id="ePz-Cb-dWk"/>
|
|
||||||
<outlet property="imageViewType" destination="0NQ-ZD-CqD" id="Cph-FN-LaY"/>
|
|
||||||
<outlet property="labelDriver" destination="TbX-e2-3QL" id="qJh-Ak-Dge"/>
|
|
||||||
<outlet property="labelDriverType" destination="jKi-Ls-7FZ" id="ZTq-pP-qUC"/>
|
|
||||||
<outlet property="labelPathName" destination="CXK-Q9-CpO" id="iVZ-cL-azB"/>
|
|
||||||
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</tableCellView>
|
</tableCellView>
|
||||||
</prototypeCellViews>
|
</prototypeCellViews>
|
||||||
@ -951,22 +1045,27 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="R3Z-g3-tYQ"/>
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="R3Z-g3-tYQ"/>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="620" id="iRQ-sz-oyv"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
<scroller key="horizontalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
||||||
<rect key="frame" x="1" y="292" width="598" height="16"/>
|
<rect key="frame" x="0.0" y="293" width="626" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
||||||
<rect key="frame" x="558" y="29" width="15" height="225"/>
|
<rect key="frame" x="610" y="28" width="16" height="281"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" wantsLayer="YES" id="xUg-Mq-OSh">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="662" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
||||||
<rect key="frame" x="285" y="150" width="30" height="30"/>
|
<rect key="frame" x="298" y="150" width="30" height="30"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
||||||
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
||||||
@ -989,7 +1088,7 @@ Gw
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="251" y="741.5"/>
|
<point key="canvasLocation" x="388" y="715.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -166,6 +166,23 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.services_json_error.desc".localized
|
descriptionText: "startup.errors.services_json_error.desc".localized
|
||||||
),
|
),
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
// Determine that the Valet configuration JSON file is valid.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
// Detect additional binaries (e.g. Composer)
|
||||||
|
Paths.shared.detectBinaryPaths()
|
||||||
|
// Load the configuration file (config.json)
|
||||||
|
Valet.shared.loadConfiguration()
|
||||||
|
// This check fails when the config is nil
|
||||||
|
return Valet.shared.config == nil
|
||||||
|
},
|
||||||
|
name: "`config.json` was valid",
|
||||||
|
titleText: "startup.errors.valet_json_invalid.title".localized,
|
||||||
|
subtitleText: "startup.errors.valet_json_invalid.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.valet_json_invalid.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
// Determine the Valet version and ensure it isn't unknown.
|
// Determine the Valet version and ensure it isn't unknown.
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
EnvironmentCheck(
|
EnvironmentCheck(
|
||||||
|
@ -36,7 +36,8 @@ struct PhpFrameworks {
|
|||||||
"statamic/cms": "Statamic",
|
"statamic/cms": "Statamic",
|
||||||
"johnpbloch/wordpress-core": "WordPress",
|
"johnpbloch/wordpress-core": "WordPress",
|
||||||
"zendframework/zendframework": "Zend",
|
"zendframework/zendframework": "Zend",
|
||||||
"zendframework/zend-mvc": "Zend"
|
"zendframework/zend-mvc": "Zend",
|
||||||
|
"typo3/cms-core": "Typo3",
|
||||||
|
|
||||||
// TODO (5.1): Handle these in v5.1
|
// TODO (5.1): Handle these in v5.1
|
||||||
// "magento/*": "Magento",
|
// "magento/*": "Magento",
|
||||||
@ -58,6 +59,10 @@ struct PhpFrameworks {
|
|||||||
"/wp-config.php",
|
"/wp-config.php",
|
||||||
"/wp-config-sample.php"
|
"/wp-config-sample.php"
|
||||||
],
|
],
|
||||||
|
"Typo3": [
|
||||||
|
"/typo3",
|
||||||
|
"/public/typo3",
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
39
phpmon/Domain/Integrations/Valet/NginxConfigParser.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// NginxConfigParser.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 15/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class NginxConfigParser {
|
||||||
|
|
||||||
|
var contents: String!
|
||||||
|
|
||||||
|
init(filePath: String) {
|
||||||
|
self.contents = try! String(contentsOfFile: filePath
|
||||||
|
.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy var isolatedVersion: String? = {
|
||||||
|
let regex = try! NSRegularExpression(
|
||||||
|
// PHP versions have (so far) never needed multiple digits for version numbers
|
||||||
|
pattern: #"(ISOLATED_PHP_VERSION=(php)?(@)?)((?<major>\d)(.)?(?<minor>\d))"#,
|
||||||
|
options: []
|
||||||
|
)
|
||||||
|
|
||||||
|
let match = regex.firstMatch(in: contents, range: NSMakeRange(0, contents.count))
|
||||||
|
|
||||||
|
if match == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let major: String = contents[Range(match!.range(withName: "major"), in: contents)!]
|
||||||
|
let minor: String = contents[Range(match!.range(withName: "minor"), in: contents)!]
|
||||||
|
|
||||||
|
return "\(major).\(minor)"
|
||||||
|
}()
|
||||||
|
}
|
134
phpmon/Domain/Integrations/Valet/SiteScanner.swift
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// ValetSiteScanner.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 19/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol SiteScanner
|
||||||
|
{
|
||||||
|
func resolveSiteCount(paths: [String]) -> Int
|
||||||
|
|
||||||
|
func resolveSitesFrom(paths: [String]) -> [ValetSite]
|
||||||
|
|
||||||
|
func resolveSite(path: String) -> ValetSite?
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeSiteScanner: SiteScanner
|
||||||
|
{
|
||||||
|
let fakes = [
|
||||||
|
ValetSite(fakeWithName: "laravel", tld: "test", secure: true, path: "~/Code/laravel/framework", linked: true),
|
||||||
|
|
||||||
|
ValetSite(fakeWithName: "tailwind", tld: "test", secure: true, path: "~/Code/tailwind/site", linked: true, constraint: "8.0"),
|
||||||
|
|
||||||
|
ValetSite(fakeWithName: "forge", tld: "test", secure: true, path: "~/Code/laravel/forge", linked: true),
|
||||||
|
|
||||||
|
ValetSite(fakeWithName: "concord", tld: "test", secure: false,
|
||||||
|
path: "~/Code/concord", linked: true, driver: "Laravel (^8.0)", constraint: "^7.4", isolated: "7.4"),
|
||||||
|
|
||||||
|
ValetSite(fakeWithName: "drupal", tld: "test", secure: false,
|
||||||
|
path: "~/Sites/drupal", linked: false, driver: "Drupal", constraint: "^7.4", isolated: "7.4"),
|
||||||
|
|
||||||
|
ValetSite(fakeWithName: "wordpress", tld: "test", secure: false,
|
||||||
|
path: "~/Sites/wordpress", linked: false, driver: "WordPress", constraint: "^7.4", isolated: "7.4")
|
||||||
|
]
|
||||||
|
|
||||||
|
func resolveSiteCount(paths: [String]) -> Int {
|
||||||
|
return fakes.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveSitesFrom(paths: [String]) -> [ValetSite] {
|
||||||
|
return fakes
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveSite(path: String) -> ValetSite? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValetSiteScanner: SiteScanner
|
||||||
|
{
|
||||||
|
func resolveSiteCount(paths: [String]) -> Int {
|
||||||
|
return paths.map { path in
|
||||||
|
|
||||||
|
let entries = try! FileManager.default
|
||||||
|
.contentsOfDirectory(atPath: path)
|
||||||
|
|
||||||
|
return entries
|
||||||
|
.map { self.isSite($0, forPath: path) }
|
||||||
|
.filter{ $0 == true}
|
||||||
|
.count
|
||||||
|
|
||||||
|
}.reduce(0, +)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveSitesFrom(paths: [String]) -> [ValetSite] {
|
||||||
|
var sites: [ValetSite] = []
|
||||||
|
|
||||||
|
paths.forEach { path in
|
||||||
|
let entries = try! FileManager.default
|
||||||
|
.contentsOfDirectory(atPath: path)
|
||||||
|
|
||||||
|
return entries.forEach {
|
||||||
|
if let site = self.resolveSite(path: "\(path)/\($0)") {
|
||||||
|
sites.append(site)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sites
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether the site can be resolved as a symbolic link or as a directory.
|
||||||
|
Regular files are ignored, and the site is added to Valet's list of sites.
|
||||||
|
*/
|
||||||
|
func resolveSite(path: String) -> ValetSite? {
|
||||||
|
// Get the TLD from the global Valet object
|
||||||
|
let tld = Valet.shared.config.tld
|
||||||
|
|
||||||
|
// See if the file is a symlink, if so, resolve it
|
||||||
|
guard let attrs = try? FileManager.default.attributesOfItem(atPath: path) else {
|
||||||
|
Log.warn("Could not parse the site: \(path), skipping!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can also determine whether the thing at the path is a directory, too
|
||||||
|
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
||||||
|
|
||||||
|
// We should also check that we can interpret the path correctly
|
||||||
|
if URL(fileURLWithPath: path).lastPathComponent == "" {
|
||||||
|
Log.warn("Could not parse the site: \(path), skipping!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == FileAttributeType.typeSymbolicLink {
|
||||||
|
return ValetSite(aliasPath: path, tld: tld)
|
||||||
|
} else if type == FileAttributeType.typeDirectory {
|
||||||
|
return ValetSite(absolutePath: path, tld: tld)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether the site can be resolved as a symbolic link or as a directory.
|
||||||
|
Regular files are ignored. Returns true if the path can be parsed.
|
||||||
|
*/
|
||||||
|
private func isSite(_ entry: String, forPath path: String) -> Bool {
|
||||||
|
let siteDir = path + "/" + entry
|
||||||
|
|
||||||
|
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
||||||
|
|
||||||
|
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
||||||
|
|
||||||
|
if type == FileAttributeType.typeSymbolicLink || type == FileAttributeType.typeDirectory {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,11 @@ import Foundation
|
|||||||
|
|
||||||
class Valet {
|
class Valet {
|
||||||
|
|
||||||
|
enum FeatureFlag {
|
||||||
|
case isolatedSites,
|
||||||
|
supportForPhp56
|
||||||
|
}
|
||||||
|
|
||||||
static let shared = Valet()
|
static let shared = Valet()
|
||||||
|
|
||||||
/// The version of Valet that was detected.
|
/// The version of Valet that was detected.
|
||||||
@ -24,28 +29,56 @@ class Valet {
|
|||||||
/// Whether we're busy with some blocking operation.
|
/// Whether we're busy with some blocking operation.
|
||||||
var isBusy: Bool = false
|
var isBusy: Bool = false
|
||||||
|
|
||||||
|
/// Various feature flags. Enabled based on the installed Valet version.
|
||||||
|
var features: [FeatureFlag] = []
|
||||||
|
|
||||||
/// When initialising the Valet singleton assume no sites loaded. We will load the version later.
|
/// When initialising the Valet singleton assume no sites loaded. We will load the version later.
|
||||||
init() {
|
init() {
|
||||||
self.version = nil
|
self.version = nil
|
||||||
self.sites = []
|
self.sites = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If marketing mode is enabled, show a list of sites that are used for promotional screenshots.
|
||||||
|
This can be done by swapping out the real Valet scanner with one that always returns a fixed
|
||||||
|
list of fake sites. You should not interact with these sites!
|
||||||
|
*/
|
||||||
|
static var siteScanner: SiteScanner {
|
||||||
|
if ProcessInfo.processInfo.environment["PHPMON_MARKETING_MODE"] != nil {
|
||||||
|
return FakeSiteScanner()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValetSiteScanner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if a particular feature is enabled.
|
||||||
|
*/
|
||||||
|
public static func enabled(feature: FeatureFlag) -> Bool {
|
||||||
|
return self.shared.features.contains(feature)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
We don't want to load the initial config.json file as soon as the class is initialised.
|
We don't want to load the initial config.json file as soon as the class is initialised.
|
||||||
|
|
||||||
Instead, we'll defer the loading of the configuration file once the initial app checks
|
Instead, we'll defer the loading of the configuration file once the initial app checks
|
||||||
have passed: if the user does not have Valet installed, we'll crash the app because we
|
have passed: otherwise the file might not exist, leading to a crash.
|
||||||
force unwrap the file. Currently, this does also mean that if the JSON is invalid or
|
|
||||||
incompatible with the `Decodable` `Valet.Configuration` class, that the app will crash.
|
Since version 5.2, it is no longer possible for an invalid file to crash the app.
|
||||||
|
If the JSON is invalid when the app launches, an alert will be presented, however.
|
||||||
*/
|
*/
|
||||||
public func loadConfiguration() {
|
public func loadConfiguration() {
|
||||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent(".config/valet/config.json")
|
.appendingPathComponent(".config/valet/config.json")
|
||||||
|
|
||||||
// TODO: (5.1) Fix loading of invalid JSON: do not crash the app
|
do {
|
||||||
config = try! JSONDecoder().decode(
|
config = try JSONDecoder().decode(
|
||||||
Valet.Configuration.self,
|
Valet.Configuration.self,
|
||||||
from: try! String(contentsOf: file, encoding: .utf8).data(using: .utf8)!
|
from: try String(contentsOf: file, encoding: .utf8).data(using: .utf8)!
|
||||||
)
|
)
|
||||||
|
} catch {
|
||||||
|
Log.err(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +87,7 @@ class Valet {
|
|||||||
(This is done to keep the startup speed as fast as possible.)
|
(This is done to keep the startup speed as fast as possible.)
|
||||||
*/
|
*/
|
||||||
public func startPreloadingSites() {
|
public func startPreloadingSites() {
|
||||||
let maximumPreload = 30
|
let maximumPreload = 50
|
||||||
let foundSites = self.countPaths()
|
let foundSites = self.countPaths()
|
||||||
if foundSites <= maximumPreload {
|
if foundSites <= maximumPreload {
|
||||||
// Preload the sites and their drivers
|
// Preload the sites and their drivers
|
||||||
@ -67,14 +100,34 @@ class Valet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Reloads the list of sites, assuming that the list isn't being reloaded at the time.
|
Reloads the list of sites, assuming that the list isn't being reloaded at the time.
|
||||||
We don't want to do duplicate or parallel work!
|
(We don't want to do duplicate or parallel work!)
|
||||||
*/
|
*/
|
||||||
public func reloadSites() {
|
public func reloadSites() {
|
||||||
|
loadConfiguration()
|
||||||
|
|
||||||
if (isBusy) {
|
if (isBusy) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePaths(tld: config.tld)
|
resolvePaths()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Depending on the version of Valet that is active, the feature set of PHP Monitor will change.
|
||||||
|
|
||||||
|
In version 6.0, support for Valet 2.x will be dropped, but until then features are evaluated by using the helper
|
||||||
|
`enabled(feature)`, which contains information about the feature set of the version of Valet that is currently
|
||||||
|
in use. This allows PHP Monitor to do different things when Valet 3.0 is enabled.
|
||||||
|
*/
|
||||||
|
public func evaluateFeatureSupport() -> Void {
|
||||||
|
let isOlderThanVersionThree = version.versionCompare("3.0") == .orderedAscending
|
||||||
|
|
||||||
|
if isOlderThanVersionThree {
|
||||||
|
self.features.append(.supportForPhp56)
|
||||||
|
} else {
|
||||||
|
Log.info("This version of Valet supports isolation.")
|
||||||
|
self.features.append(.isolatedSites)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,6 +136,10 @@ class Valet {
|
|||||||
installed is not recent enough.
|
installed is not recent enough.
|
||||||
*/
|
*/
|
||||||
public func validateVersion() -> Void {
|
public func validateVersion() -> Void {
|
||||||
|
// 1. Evaluate feature support
|
||||||
|
Valet.shared.evaluateFeatureSupport()
|
||||||
|
|
||||||
|
// 2. Notify user if the version is too old
|
||||||
if version.versionCompare(Constants.MinimumRecommendedValetVersion) == .orderedAscending {
|
if version.versionCompare(Constants.MinimumRecommendedValetVersion) == .orderedAscending {
|
||||||
let version = version
|
let version = version
|
||||||
Log.warn("Valet version \(version!) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
Log.warn("Valet version \(version!) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
||||||
@ -104,82 +161,28 @@ class Valet {
|
|||||||
Returns a count of how many sites are linked and parked.
|
Returns a count of how many sites are linked and parked.
|
||||||
*/
|
*/
|
||||||
private func countPaths() -> Int {
|
private func countPaths() -> Int {
|
||||||
var count = 0
|
return Self.siteScanner
|
||||||
for path in config.paths {
|
.resolveSiteCount(paths: config.paths)
|
||||||
let entries = try! FileManager.default.contentsOfDirectory(atPath: path)
|
|
||||||
for entry in entries {
|
|
||||||
if resolveSite(entry, forPath: path) {
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Resolves all paths and creates linked or parked site instances that can be referenced later.
|
Resolves all paths and creates linked or parked site instances that can be referenced later.
|
||||||
*/
|
*/
|
||||||
private func resolvePaths(tld: String) {
|
private func resolvePaths() {
|
||||||
isBusy = true
|
isBusy = true
|
||||||
|
|
||||||
sites = []
|
sites = Self.siteScanner
|
||||||
|
.resolveSitesFrom(paths: config.paths)
|
||||||
|
.sorted { $0.absolutePath < $1.absolutePath }
|
||||||
|
|
||||||
for path in config.paths {
|
if let defaultPath = Valet.shared.config.defaultSite,
|
||||||
let entries = try! FileManager.default.contentsOfDirectory(atPath: path)
|
let site = ValetSiteScanner().resolveSite(path: defaultPath) {
|
||||||
for entry in entries {
|
sites.insert(site, at: 0)
|
||||||
resolvePath(entry, forPath: path, tld: tld)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sites = sites.sorted { $0.absolutePath < $1.absolutePath }
|
|
||||||
|
|
||||||
isBusy = false
|
isBusy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Determines whether the site can be resolved as a symbolic link or as a directory.
|
|
||||||
Regular files are ignored. Returns true if the path can be parsed.
|
|
||||||
*/
|
|
||||||
private func resolveSite(_ entry: String, forPath path: String) -> Bool {
|
|
||||||
let siteDir = path + "/" + entry
|
|
||||||
|
|
||||||
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
|
||||||
|
|
||||||
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
|
||||||
|
|
||||||
if type == FileAttributeType.typeSymbolicLink || type == FileAttributeType.typeDirectory {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines whether the site can be resolved as a symbolic link or as a directory.
|
|
||||||
Regular files are ignored, and the site is added to Valet's list of sites.
|
|
||||||
*/
|
|
||||||
private func resolvePath(_ entry: String, forPath path: String, tld: String) {
|
|
||||||
let siteDir = path + "/" + entry
|
|
||||||
|
|
||||||
// See if the file is a symlink, if so, resolve it
|
|
||||||
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
|
||||||
|
|
||||||
// We can also determine whether the thing at the path is a directory, too
|
|
||||||
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
|
||||||
|
|
||||||
// We should also check that we can interpret the path correctly
|
|
||||||
if URL(fileURLWithPath: siteDir).lastPathComponent == "" {
|
|
||||||
Log.warn("Could not parse the site: \(siteDir), skipping!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if type == FileAttributeType.typeSymbolicLink {
|
|
||||||
sites.append(ValetSite(aliasPath: siteDir, tld: tld))
|
|
||||||
} else if type == FileAttributeType.typeDirectory {
|
|
||||||
sites.append(ValetSite(absolutePath: siteDir, tld: tld))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Configuration: Decodable {
|
struct Configuration: Decodable {
|
||||||
/// Top level domain suffix. Usually "test" but can be set to something else.
|
/// Top level domain suffix. Usually "test" but can be set to something else.
|
||||||
/// - Important: Does not include the actual dot. ("test", not ".test"!)
|
/// - Important: Does not include the actual dot. ("test", not ".test"!)
|
||||||
|
44
phpmon/Domain/Integrations/Valet/ValetSite+Fake.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// ValetSite+Fake.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 19/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ValetSite {
|
||||||
|
|
||||||
|
convenience init(
|
||||||
|
fakeWithName name: String,
|
||||||
|
tld: String,
|
||||||
|
secure: Bool,
|
||||||
|
path: String,
|
||||||
|
linked: Bool,
|
||||||
|
driver: String = "Laravel (^9.0)",
|
||||||
|
constraint: String = "^8.1",
|
||||||
|
isolated: String? = nil
|
||||||
|
) {
|
||||||
|
self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false)
|
||||||
|
self.secured = secure
|
||||||
|
self.composerPhp = constraint
|
||||||
|
|
||||||
|
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
||||||
|
.map { string in
|
||||||
|
return PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
||||||
|
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
.count > 0
|
||||||
|
}.contains(true)
|
||||||
|
|
||||||
|
self.driver = driver
|
||||||
|
self.driverDeterminedByComposer = true
|
||||||
|
if linked {
|
||||||
|
self.aliasPath = self.absolutePath
|
||||||
|
}
|
||||||
|
if let isolated = isolated {
|
||||||
|
self.isolatedPhpVersion = PhpInstallation(isolated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,10 +11,10 @@ import Foundation
|
|||||||
class ValetSite {
|
class ValetSite {
|
||||||
|
|
||||||
/// Name of the site. Does not include the TLD.
|
/// Name of the site. Does not include the TLD.
|
||||||
var name: String!
|
var name: String
|
||||||
|
|
||||||
/// The absolute path to the directory that is served.
|
/// The absolute path to the directory that is served.
|
||||||
var absolutePath: String!
|
var absolutePath: String
|
||||||
|
|
||||||
/// The absolute path to the directory that is served,
|
/// The absolute path to the directory that is served,
|
||||||
/// replacing the user's home folder with ~.
|
/// replacing the user's home folder with ~.
|
||||||
@ -23,6 +23,12 @@ class ValetSite {
|
|||||||
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
/// The TLD used to locate this site.
|
||||||
|
var tld: String = "test"
|
||||||
|
|
||||||
|
/// The PHP version that is being used to serve this site specifically (if not global).
|
||||||
|
var isolatedPhpVersion: PhpInstallation?
|
||||||
|
|
||||||
/// Location of the alias. If set, this is a linked domain.
|
/// Location of the alias. If set, this is a linked domain.
|
||||||
var aliasPath: String?
|
var aliasPath: String?
|
||||||
|
|
||||||
@ -47,6 +53,12 @@ class ValetSite {
|
|||||||
/// How the PHP version was determined.
|
/// How the PHP version was determined.
|
||||||
var composerPhpSource: VersionSource = .unknown
|
var composerPhpSource: VersionSource = .unknown
|
||||||
|
|
||||||
|
/// Which version of PHP is actually used to serve this site.
|
||||||
|
var servingPhpVersion: String {
|
||||||
|
return self.isolatedPhpVersion?.versionNumber.homebrewVersion
|
||||||
|
?? PhpEnv.phpInstall.version.short
|
||||||
|
}
|
||||||
|
|
||||||
enum VersionSource: String {
|
enum VersionSource: String {
|
||||||
case unknown = "unknown"
|
case unknown = "unknown"
|
||||||
case require = "require"
|
case require = "require"
|
||||||
@ -54,34 +66,59 @@ class ValetSite {
|
|||||||
case valetphprc = "valetphprc"
|
case valetphprc = "valetphprc"
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {}
|
init(
|
||||||
|
name: String,
|
||||||
|
tld: String,
|
||||||
|
absolutePath: String,
|
||||||
|
aliasPath: String? = nil,
|
||||||
|
makeDeterminations: Bool = true
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.tld = tld
|
||||||
|
self.absolutePath = absolutePath
|
||||||
|
self.aliasPath = aliasPath
|
||||||
|
self.secured = false
|
||||||
|
|
||||||
|
if makeDeterminations {
|
||||||
|
determineSecured()
|
||||||
|
determineComposerPhpVersion()
|
||||||
|
determineDriver()
|
||||||
|
determineIsolated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
convenience init(absolutePath: String, tld: String) {
|
convenience init(absolutePath: String, tld: String) {
|
||||||
self.init()
|
let name = URL(fileURLWithPath: absolutePath).lastPathComponent
|
||||||
self.absolutePath = absolutePath
|
self.init(name: name, tld: tld, absolutePath: absolutePath)
|
||||||
self.name = URL(fileURLWithPath: absolutePath).lastPathComponent
|
|
||||||
self.aliasPath = nil
|
|
||||||
determineSecured(tld)
|
|
||||||
determineComposerPhpVersion()
|
|
||||||
determineDriver()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
convenience init(aliasPath: String, tld: String) {
|
convenience init(aliasPath: String, tld: String) {
|
||||||
self.init()
|
let name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
||||||
self.absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath)
|
let absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath)
|
||||||
self.name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
self.init(name: name, tld: tld, absolutePath: absolutePath, aliasPath: aliasPath)
|
||||||
self.aliasPath = aliasPath
|
}
|
||||||
determineSecured(tld)
|
|
||||||
determineComposerPhpVersion()
|
/**
|
||||||
determineDriver()
|
Determine whether a site is isolated.
|
||||||
|
*/
|
||||||
|
public func determineIsolated() {
|
||||||
|
if let version = ValetSite.isolatedVersion("~/.config/valet/Nginx/\(self.name).\(self.tld)") {
|
||||||
|
if (!PhpEnv.shared.cachedPhpInstallations.keys.contains(version)) {
|
||||||
|
Log.err("The PHP version \(version) is isolated for the site \(self.name) but that PHP version is unavailable.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isolatedPhpVersion = PhpEnv.shared.cachedPhpInstallations[version]
|
||||||
|
} else {
|
||||||
|
self.isolatedPhpVersion = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a certificate file can be found in the `valet/Certificates` directory.
|
Checks if a certificate file can be found in the `valet/Certificates` directory.
|
||||||
- Note: The file is not validated, only its presence is checked.
|
- Note: The file is not validated, only its presence is checked.
|
||||||
*/
|
*/
|
||||||
public func determineSecured(_ tld: String) {
|
public func determineSecured() {
|
||||||
secured = Filesystem.fileExists("~/.config/valet/Certificates/\(self.name!).\(tld).key")
|
secured = Filesystem.fileExists("~/.config/valet/Certificates/\(self.name).\(self.tld).key")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +184,7 @@ class ValetSite {
|
|||||||
as well as the requested PHP version. If no composer.json file is found, nothing happens.
|
as well as the requested PHP version. If no composer.json file is found, nothing happens.
|
||||||
*/
|
*/
|
||||||
private func determineComposerInformation() {
|
private func determineComposerInformation() {
|
||||||
let path = "\(absolutePath!)/composer.json"
|
let path = "\(absolutePath)/composer.json"
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if Filesystem.fileExists(path) {
|
if Filesystem.fileExists(path) {
|
||||||
@ -168,7 +205,7 @@ class ValetSite {
|
|||||||
Checks the contents of the .valetphprc file and determine the version, if possible.
|
Checks the contents of the .valetphprc file and determine the version, if possible.
|
||||||
*/
|
*/
|
||||||
private func determineValetPhpFileInfo() {
|
private func determineValetPhpFileInfo() {
|
||||||
let path = "\(absolutePath!)/.valetphprc"
|
let path = "\(absolutePath)/.valetphprc"
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if Filesystem.fileExists(path) {
|
if Filesystem.fileExists(path) {
|
||||||
@ -182,4 +219,16 @@ class ValetSite {
|
|||||||
Log.err("Something went wrong parsing the .valetphprc file")
|
Log.err("Something went wrong parsing the .valetphprc file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: File Parsing
|
||||||
|
|
||||||
|
public static func isolatedVersion(_ filePath: String) -> String? {
|
||||||
|
if Filesystem.fileExists(filePath) {
|
||||||
|
return NginxConfigParser
|
||||||
|
.init(filePath: filePath)
|
||||||
|
.isolatedVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,13 @@ extension MainMenu {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncExecution {
|
Actions.fixMyValet {
|
||||||
Actions.fixMyValet()
|
DispatchQueue.main.async {
|
||||||
} success: {
|
if previousVersion == PhpEnv.brewPhpVersion {
|
||||||
if previousVersion == PhpEnv.brewPhpVersion {
|
self.presentAlertForSameVersion()
|
||||||
self.presentAlertForSameVersion()
|
} else {
|
||||||
} else {
|
self.presentAlertForDifferentVersion(version: previousVersion)
|
||||||
self.presentAlertForDifferentVersion(version: previousVersion)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,6 @@ extension MainMenu {
|
|||||||
Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version!)")
|
Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version!)")
|
||||||
}
|
}
|
||||||
|
|
||||||
Paths.shared.detectBinaryPaths()
|
|
||||||
|
|
||||||
Valet.shared.loadConfiguration()
|
|
||||||
Valet.shared.validateVersion()
|
Valet.shared.validateVersion()
|
||||||
Valet.shared.startPreloadingSites()
|
Valet.shared.startPreloadingSites()
|
||||||
|
|
||||||
|
@ -340,6 +340,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func menuDidClose(_ menu: NSMenu) {
|
func menuDidClose(_ menu: NSMenu) {
|
||||||
|
@ -45,20 +45,16 @@ class ServicesView: NSView, XibLoadable {
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDraw() {
|
|
||||||
super.viewWillDraw()
|
|
||||||
Task { await self.loadData() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateInformation() {
|
@objc func updateInformation() {
|
||||||
Task { await self.loadData() }
|
self.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadData() async {
|
func loadData() {
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
|
||||||
let services = await HomebrewService.loadAll()
|
|
||||||
ServicesView.services = Dictionary(uniqueKeysWithValues: services.map{ ($0.name, $0) })
|
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
self.applyAllInfoFieldsFromCachedValue()
|
||||||
|
HomebrewService.loadAll { services in
|
||||||
|
ServicesView.services = Dictionary(uniqueKeysWithValues: services.map{ ($0.name, $0) })
|
||||||
|
self.applyAllInfoFieldsFromCachedValue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyAllInfoFieldsFromCachedValue() {
|
func applyAllInfoFieldsFromCachedValue() {
|
||||||
|
@ -97,15 +97,20 @@ class StatusMenu : NSMenu {
|
|||||||
func addFirstAidAndServicesMenuItems() {
|
func addFirstAidAndServicesMenuItems() {
|
||||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||||
let servicesMenu = NSMenu()
|
let servicesMenu = NSMenu()
|
||||||
servicesMenu.addItem(NSMenuItem(
|
|
||||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
|
||||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: "")
|
|
||||||
)
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(
|
let fixMyValetMenuItem = NSMenuItem(
|
||||||
title: "mi_fix_brew_permissions".localized(),
|
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||||
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: "")
|
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(NSMenuItem.separator())
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||||
@ -139,7 +144,7 @@ class StatusMenu : NSMenu {
|
|||||||
|
|
||||||
// Get the short and long version
|
// Get the short and long version
|
||||||
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
|
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
|
||||||
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.longVersion
|
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.versionNumber
|
||||||
|
|
||||||
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
||||||
let versionString = long ? longVersion.toString() : shortVersion
|
let versionString = long ? longVersion.toString() : shortVersion
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
|
||||||
import Carbon
|
import Carbon
|
||||||
|
|
||||||
class PrefsVC: NSViewController {
|
class PrefsVC: NSViewController {
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HotKey
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class HotkeyPreferenceView: NSView, XibLoadable {
|
class HotkeyPreferenceView: NSView, XibLoadable {
|
||||||
|
@ -63,7 +63,9 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Shell.run("cd '\(path)' && \(Paths.valet) link '\(name)'", requiresPath: true)
|
// Adding `valet links` is a workaround for Valet malforming the config.json file
|
||||||
|
// TODO: I will have to investigate and report this behaviour if possible
|
||||||
|
Shell.run("cd '\(path)' && \(Paths.valet) link '\(name)' && valet links", requiresPath: true)
|
||||||
|
|
||||||
self.dismissView(outcome: .OK)
|
self.dismissView(outcome: .OK)
|
||||||
|
|
||||||
|
14
phpmon/Domain/SiteList/Cells/SiteListCellProtocol.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// SiteListCellProtocol.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 03/12/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
protocol SiteListCellProtocol {
|
||||||
|
func populateCell(with site: ValetSite)
|
||||||
|
}
|
33
phpmon/Domain/SiteList/Cells/SiteListKindCell.swift
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// SiteListTypeCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SiteListKindCell: NSTableCellView, SiteListCellProtocol
|
||||||
|
{
|
||||||
|
static let reusableName = "siteListKindCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var imageViewType: NSImageView!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
// If the `aliasPath` is nil, we're dealing with a parked site (otherwise: linked).
|
||||||
|
imageViewType.image = NSImage(
|
||||||
|
named: site.aliasPath == nil
|
||||||
|
? "IconParked"
|
||||||
|
: "IconLinked"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unless, of course, this is a default site
|
||||||
|
if site.absolutePath == Valet.shared.config.defaultSite {
|
||||||
|
imageViewType.image = NSImage(named: "IconDefault")
|
||||||
|
}
|
||||||
|
|
||||||
|
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
||||||
|
}
|
||||||
|
}
|
26
phpmon/Domain/SiteList/Cells/SiteListNameCell.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// SiteListNameCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SiteListNameCell: NSTableCellView, SiteListCellProtocol
|
||||||
|
{
|
||||||
|
static let reusableName = "siteListNameCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var labelSiteName: NSTextField!
|
||||||
|
@IBOutlet weak var labelPathName: NSTextField!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
// Show the name of the site (including tld)
|
||||||
|
labelSiteName.stringValue = "\(site.name).\(Valet.shared.config.tld)"
|
||||||
|
|
||||||
|
// Show the absolute path, except make sure to replace the /Users/username segment with ~ for readability
|
||||||
|
labelPathName.stringValue = site.absolutePathRelative
|
||||||
|
}
|
||||||
|
}
|
90
phpmon/Domain/SiteList/Cells/SiteListPhpCell.swift
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// SiteListPhpCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
|
||||||
|
{
|
||||||
|
static let reusableName = "siteListPhpCell"
|
||||||
|
|
||||||
|
var site: ValetSite? = nil
|
||||||
|
|
||||||
|
@IBOutlet weak var buttonPhpVersion: NSButton!
|
||||||
|
@IBOutlet weak var imageViewPhpVersionOK: NSImageView!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
self.site = site
|
||||||
|
|
||||||
|
buttonPhpVersion.title = " PHP \(site.servingPhpVersion)"
|
||||||
|
|
||||||
|
if site.isolatedPhpVersion != nil {
|
||||||
|
imageViewPhpVersionOK.isHidden = false
|
||||||
|
imageViewPhpVersionOK.image = NSImage(named: "Isolated")
|
||||||
|
} else {
|
||||||
|
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
||||||
|
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func pressedPhpVersion(_ sender: Any) {
|
||||||
|
guard let site = self.site else { return }
|
||||||
|
|
||||||
|
let alert = NSAlert.init()
|
||||||
|
alert.alertStyle = .informational
|
||||||
|
|
||||||
|
var information = ""
|
||||||
|
|
||||||
|
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
|
||||||
|
}).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.siteListWindowController!.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.siteListWindowController!.window!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
phpmon/Domain/SiteList/Cells/SiteListTLSCell.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// SiteListNameCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SiteListTLSCell: NSTableCellView, SiteListCellProtocol
|
||||||
|
{
|
||||||
|
static let reusableName = "siteListTLSCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var imageViewLock: NSImageView!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
// Show the green or red lock based on whether the site was secured
|
||||||
|
imageViewLock.contentTintColor = site.secured
|
||||||
|
? NSColor(named: "IconColorGreen") // green
|
||||||
|
: NSColor(named: "IconColorRed")
|
||||||
|
}
|
||||||
|
}
|
31
phpmon/Domain/SiteList/Cells/SiteListTypeCell.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// SiteListTypeCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SiteListTypeCell: NSTableCellView, SiteListCellProtocol
|
||||||
|
{
|
||||||
|
static let reusableName = "siteListTypeCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var labelDriver: NSTextField!
|
||||||
|
@IBOutlet weak var labelPhpVersion: NSTextField!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
labelDriver.stringValue = site.driver ?? "driver.not_detected".localized
|
||||||
|
|
||||||
|
// Determine the Laravel version
|
||||||
|
if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") {
|
||||||
|
let constraint = site.notableComposerDependencies["laravel/framework"]!
|
||||||
|
labelDriver.stringValue = "Laravel (\(constraint))"
|
||||||
|
}
|
||||||
|
|
||||||
|
// PHP version
|
||||||
|
labelPhpVersion.stringValue = site.composerPhp == "???" ? "Any PHP" : "PHP \(site.composerPhp)"
|
||||||
|
}
|
||||||
|
}
|
@ -1,111 +0,0 @@
|
|||||||
//
|
|
||||||
// SiteListCell.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 03/12/2021.
|
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class SiteListCell: NSTableCellView
|
|
||||||
{
|
|
||||||
var site: ValetSite? = nil
|
|
||||||
|
|
||||||
@IBOutlet weak var labelSiteName: NSTextField!
|
|
||||||
@IBOutlet weak var labelPathName: NSTextField!
|
|
||||||
@IBOutlet weak var labelDriverType: NSTextField!
|
|
||||||
|
|
||||||
@IBOutlet weak var imageViewLock: NSImageView!
|
|
||||||
@IBOutlet weak var imageViewType: NSImageView!
|
|
||||||
|
|
||||||
@IBOutlet weak var labelDriver: NSTextField!
|
|
||||||
|
|
||||||
@IBOutlet weak var buttonPhpVersion: NSButton!
|
|
||||||
@IBOutlet weak var imageViewPhpVersionOK: NSImageView!
|
|
||||||
|
|
||||||
override func draw(_ dirtyRect: NSRect) {
|
|
||||||
super.draw(dirtyRect)
|
|
||||||
}
|
|
||||||
|
|
||||||
func populateCell(with site: ValetSite) {
|
|
||||||
self.site = site
|
|
||||||
|
|
||||||
// Make sure to show the TLD
|
|
||||||
labelSiteName.stringValue = "\(site.name!).\(Valet.shared.config.tld)"
|
|
||||||
|
|
||||||
// Show the absolute path, except make sure to replace the /Users/username segment with ~ for readability
|
|
||||||
labelPathName.stringValue = site.absolutePathRelative
|
|
||||||
|
|
||||||
// If the `aliasPath` is nil, we're dealing with a parked site (otherwise: linked).
|
|
||||||
imageViewType.image = NSImage(
|
|
||||||
named: site.aliasPath == nil
|
|
||||||
? "IconParked"
|
|
||||||
: "IconLinked"
|
|
||||||
)
|
|
||||||
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
|
||||||
|
|
||||||
// Show the green or red lock based on whether the site was secured
|
|
||||||
imageViewLock.contentTintColor = site.secured ?
|
|
||||||
NSColor(named: "IconColorGreen") // green
|
|
||||||
: NSColor(named: "IconColorRed")
|
|
||||||
|
|
||||||
// Show the current driver
|
|
||||||
labelDriverType.stringValue = site.driverDeterminedByComposer
|
|
||||||
? "Project Type".uppercased()
|
|
||||||
: "Driver Type".uppercased()
|
|
||||||
|
|
||||||
labelDriver.stringValue = site.driver ?? "driver.not_detected".localized
|
|
||||||
|
|
||||||
// Determine the Laravel version
|
|
||||||
if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") {
|
|
||||||
let constraint = site.notableComposerDependencies["laravel/framework"]!
|
|
||||||
labelDriver.stringValue = "Laravel (\(constraint))"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the PHP version
|
|
||||||
buttonPhpVersion.title = " PHP \(site.composerPhp) "
|
|
||||||
buttonPhpVersion.isHidden = (site.composerPhp == "???")
|
|
||||||
|
|
||||||
|
|
||||||
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func pressedPhpVersion(_ sender: Any) {
|
|
||||||
guard let site = self.site else { return }
|
|
||||||
|
|
||||||
let alert = NSAlert.init()
|
|
||||||
alert.alertStyle = .informational
|
|
||||||
|
|
||||||
alert.messageText = "alert.composer_php_requirement.title"
|
|
||||||
.localized("\(site.name!).\(Valet.shared.config.tld)", site.composerPhp)
|
|
||||||
alert.informativeText = "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
|
|
||||||
.localized
|
|
||||||
|
|
||||||
alert.addButton(withTitle: "site_link.close".localized)
|
|
||||||
|
|
||||||
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
|
|
||||||
var map: [Int: String] = [:]
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}).forEach { version in
|
|
||||||
alert.addButton(withTitle: "site_link.switch_to_php".localized(version.homebrewVersion))
|
|
||||||
map[mapIndex] = version.homebrewVersion
|
|
||||||
mapIndex += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
alert.beginSheetModal(for: App.shared.siteListWindowController!.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,12 @@ extension SiteListVC {
|
|||||||
let originalSecureStatus = selectedSite!.secured
|
let originalSecureStatus = selectedSite!.secured
|
||||||
let action = selectedSite!.secured ? "unsecure" : "secure"
|
let action = selectedSite!.secured ? "unsecure" : "secure"
|
||||||
let selectedSite = selectedSite!
|
let selectedSite = selectedSite!
|
||||||
let command = "cd '\(selectedSite.absolutePath!)' && sudo \(Paths.valet) \(action) && exit;"
|
let command = "cd '\(selectedSite.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
||||||
|
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Shell.run(command, requiresPath: true)
|
Shell.run(command, requiresPath: true)
|
||||||
} completion: { [self] in
|
} completion: { [self] in
|
||||||
selectedSite.determineSecured(Valet.shared.config.tld)
|
selectedSite.determineSecured()
|
||||||
if selectedSite.secured == originalSecureStatus {
|
if selectedSite.secured == originalSecureStatus {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
@ -36,13 +36,13 @@ extension SiteListVC {
|
|||||||
title: "site_list.alerts_status_changed.title".localized,
|
title: "site_list.alerts_status_changed.title".localized,
|
||||||
subtitle: "site_list.alerts_status_changed.desc"
|
subtitle: "site_list.alerts_status_changed.desc"
|
||||||
.localized(
|
.localized(
|
||||||
"\(selectedSite.name!).\(Valet.shared.config.tld)",
|
"\(selectedSite.name).\(Valet.shared.config.tld)",
|
||||||
newState
|
newState
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0])
|
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
|
||||||
tableView.deselectRow(rowToReload)
|
tableView.deselectRow(rowToReload)
|
||||||
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ extension SiteListVC {
|
|||||||
|
|
||||||
@objc func openInBrowser() {
|
@objc func openInBrowser() {
|
||||||
let prefix = selectedSite!.secured ? "https://" : "http://"
|
let prefix = selectedSite!.secured ? "https://" : "http://"
|
||||||
let url = URL(string: "\(prefix)\(selectedSite!.name!).\(Valet.shared.config.tld)")
|
let url = URL(string: "\(prefix)\(selectedSite!.name).\(Valet.shared.config.tld)")
|
||||||
if url != nil {
|
if url != nil {
|
||||||
NSWorkspace.shared.open(url!)
|
NSWorkspace.shared.open(url!)
|
||||||
} else {
|
} else {
|
||||||
@ -65,16 +65,41 @@ extension SiteListVC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func openInFinder() {
|
@objc func openInFinder() {
|
||||||
Shell.run("open '\(selectedSite!.absolutePath!)'")
|
Shell.run("open '\(selectedSite!.absolutePath)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openInTerminal() {
|
@objc func openInTerminal() {
|
||||||
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath!)'")
|
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||||
guard let editor = sender.editor else { return }
|
guard let editor = sender.editor else { return }
|
||||||
editor.openDirectory(file: selectedSite!.absolutePath!)
|
editor.openDirectory(file: selectedSite!.absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func isolateSite(sender: PhpMenuItem) {
|
||||||
|
let command = "cd '\(selectedSite!.absolutePath)' && sudo \(Paths.valet) isolate php@\(sender.version) && exit;"
|
||||||
|
|
||||||
|
self.performAction(command: command) {
|
||||||
|
self.selectedSite!.determineIsolated()
|
||||||
|
|
||||||
|
if self.selectedSite!.isolatedPhpVersion == nil {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "site_list.alerts_isolation_failed.title".localized,
|
||||||
|
subtitle: "site_list.alerts_isolation_failed.subtitle".localized,
|
||||||
|
description: "site_list.alerts_isolation_failed.desc".localized(command)
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func removeIsolatedSite() {
|
||||||
|
self.performAction(command: "cd '\(selectedSite!.absolutePath)' && sudo \(Paths.valet) unisolate && exit;") {
|
||||||
|
self.selectedSite!.isolatedPhpVersion = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func unlinkSite() {
|
@objc func unlinkSite() {
|
||||||
@ -94,10 +119,23 @@ extension SiteListVC {
|
|||||||
secondButtonTitle: "Cancel",
|
secondButtonTitle: "Cancel",
|
||||||
style: .critical,
|
style: .critical,
|
||||||
onFirstButtonPressed: {
|
onFirstButtonPressed: {
|
||||||
Shell.run("valet unlink '\(site.name!)'", requiresPath: true)
|
Shell.run("valet unlink '\(site.name)'", requiresPath: true)
|
||||||
self.reloadSites()
|
self.reloadSites()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func performAction(command: String, beforeCellReload: @escaping () -> Void) {
|
||||||
|
let rowToReload = tableView.selectedRow
|
||||||
|
|
||||||
|
waitAndExecute {
|
||||||
|
Shell.run(command, requiresPath: true)
|
||||||
|
} completion: { [self] in
|
||||||
|
beforeCellReload()
|
||||||
|
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
|
||||||
|
tableView.deselectRow(rowToReload)
|
||||||
|
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,12 @@ extension SiteListVC {
|
|||||||
addDetectedApps(to: menu)
|
addDetectedApps(to: menu)
|
||||||
addSeparator(to: menu)
|
addSeparator(to: menu)
|
||||||
|
|
||||||
|
if Valet.enabled(feature: .isolatedSites) {
|
||||||
|
addIsolate(to: menu, with: site)
|
||||||
|
} else {
|
||||||
|
addDisabledIsolation(to: menu)
|
||||||
|
}
|
||||||
|
|
||||||
addUnlink(to: menu, with: site)
|
addUnlink(to: menu, with: site)
|
||||||
addToggleSecure(to: menu, with: site)
|
addToggleSecure(to: menu, with: site)
|
||||||
|
|
||||||
@ -76,6 +82,37 @@ extension SiteListVC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func addDisabledIsolation(to menu: NSMenu) {
|
||||||
|
menu.addItem(withTitle: "site_list.isolation_unavailable".localized, action: nil, keyEquivalent: "")
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addIsolate(to menu: NSMenu, with site: ValetSite) {
|
||||||
|
if site.isolatedPhpVersion == nil {
|
||||||
|
// ISOLATION POSSIBLE
|
||||||
|
let isolationMenuItem = NSMenuItem(title:"site_list.isolate".localized, action: nil, keyEquivalent: "")
|
||||||
|
let submenu = NSMenu()
|
||||||
|
submenu.addItem(withTitle: "Choose a PHP version", action: nil, keyEquivalent: "")
|
||||||
|
for version in PhpEnv.shared.availablePhpVersions.reversed() {
|
||||||
|
let item = PhpMenuItem(title: "Always use PHP \(version)", action: #selector(self.isolateSite), keyEquivalent: "")
|
||||||
|
item.version = version
|
||||||
|
submenu.addItem(item)
|
||||||
|
}
|
||||||
|
menu.setSubmenu(submenu, for: isolationMenuItem)
|
||||||
|
|
||||||
|
menu.addItem(isolationMenuItem)
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
} else {
|
||||||
|
// REMOVE ISOLATION POSSIBLE
|
||||||
|
menu.addItem(
|
||||||
|
withTitle: "site_list.remove_isolation".localized,
|
||||||
|
action: #selector(self.removeIsolatedSite),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func addToggleSecure(to menu: NSMenu, with site: ValetSite) {
|
private func addToggleSecure(to menu: NSMenu, with site: ValetSite) {
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: site.secured
|
withTitle: site.secured
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
|
||||||
import Carbon
|
import Carbon
|
||||||
|
|
||||||
class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||||
@ -27,6 +26,9 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
return App.shared.detectedApplications
|
return App.shared.detectedApplications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The last sort descriptor used.
|
||||||
|
var sortDescriptor: NSSortDescriptor? = nil
|
||||||
|
|
||||||
/// String that was last searched for. Empty by default.
|
/// String that was last searched for. Empty by default.
|
||||||
var lastSearchedFor = ""
|
var lastSearchedFor = ""
|
||||||
|
|
||||||
@ -145,6 +147,28 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applySortDescriptor(_ descriptor: NSSortDescriptor) {
|
||||||
|
sortDescriptor = descriptor
|
||||||
|
|
||||||
|
var sorted = self.sites
|
||||||
|
|
||||||
|
switch descriptor.key {
|
||||||
|
case "Secure":
|
||||||
|
sorted = self.sites.sorted { $0.secured && !$1.secured }; break
|
||||||
|
case "Domain":
|
||||||
|
sorted = self.sites.sorted { $0.absolutePath < $1.absolutePath }; break
|
||||||
|
case "PHP":
|
||||||
|
sorted = self.sites.sorted { $0.servingPhpVersion < $1.servingPhpVersion }; break
|
||||||
|
case "Kind":
|
||||||
|
sorted = self.sites.sorted { ($0.aliasPath == nil) && !($1.aliasPath == nil) }; break
|
||||||
|
case "Type":
|
||||||
|
sorted = self.sites.sorted { $0.driver ?? "ZZZ" < $1.driver ?? "ZZZ" }; break
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sites = descriptor.ascending ? sorted.reversed() : sorted
|
||||||
|
}
|
||||||
|
|
||||||
func addedNewSite(name: String, secure: Bool) {
|
func addedNewSite(name: String, secure: Bool) {
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Valet.shared.reloadSites()
|
Valet.shared.reloadSites()
|
||||||
@ -173,14 +197,32 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
return sites.count
|
return sites.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
|
||||||
|
guard let sortDescriptor = tableView.sortDescriptors.first else { return }
|
||||||
|
// Kinda scuffed way of applying sort descriptors here, but it works.
|
||||||
|
Log.info("Applying sort descriptor for column: \(sortDescriptor.key ?? "Unknown")")
|
||||||
|
applySortDescriptor(sortDescriptor)
|
||||||
|
searchedFor(text: lastSearchedFor)
|
||||||
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
guard let userCell = tableView.makeView(
|
let mapping: [String: String] = [
|
||||||
withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "siteItem"), owner: self
|
"TLS": SiteListTLSCell.reusableName,
|
||||||
) as? SiteListCell else { return nil }
|
"DOMAIN": SiteListNameCell.reusableName,
|
||||||
|
"ENVIRONMENT": SiteListPhpCell.reusableName,
|
||||||
|
"KIND": SiteListKindCell.reusableName,
|
||||||
|
"TYPE": SiteListTypeCell.reusableName,
|
||||||
|
]
|
||||||
|
|
||||||
|
let columnName = tableColumn!.identifier.rawValue
|
||||||
|
let identifier = NSUserInterfaceItemIdentifier(rawValue: mapping[columnName]!)
|
||||||
|
|
||||||
|
guard let userCell = tableView.makeView(withIdentifier: identifier, owner: self)
|
||||||
|
as? SiteListCellProtocol else { return nil }
|
||||||
|
|
||||||
userCell.populateCell(with: sites[row])
|
userCell.populateCell(with: sites[row])
|
||||||
|
|
||||||
return userCell
|
return userCell as? NSView
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||||
@ -205,9 +247,14 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
if searchString.isEmpty {
|
if searchString.isEmpty {
|
||||||
sites = Valet.shared.sites
|
sites = Valet.shared.sites
|
||||||
|
|
||||||
|
if let sortDescriptor = sortDescriptor {
|
||||||
|
self.applySortDescriptor(sortDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,6 +268,10 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
}.contains(false)
|
}.contains(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if let sortDescriptor = sortDescriptor {
|
||||||
|
self.applySortDescriptor(sortDescriptor)
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,12 @@
|
|||||||
"mi_manage_services" = "Manage Services";
|
"mi_manage_services" = "Manage Services";
|
||||||
"mi_restart_all_services" = "Restart All Services";
|
"mi_restart_all_services" = "Restart All Services";
|
||||||
"mi_stop_all_services" = "Stop All Services";
|
"mi_stop_all_services" = "Stop All Services";
|
||||||
|
|
||||||
"mi_fix_my_valet" = "Fix My Valet...";
|
"mi_fix_my_valet" = "Fix My Valet...";
|
||||||
|
"mi_fix_my_valet_tooltip" = "Something wrong with your Valet installation? Try PHP Monitor’s automatic fixes that’ll get you back up and running in no time!";
|
||||||
"mi_fix_brew_permissions" = "Restore Homebrew Permissions...";
|
"mi_fix_brew_permissions" = "Restore Homebrew Permissions...";
|
||||||
|
"mi_fix_brew_permissions_tooltip" = "Having permission issues when running `brew upgrade`? PHP Monitor to the rescue!";
|
||||||
|
|
||||||
"mi_php_refresh" = "Refresh Information";
|
"mi_php_refresh" = "Refresh Information";
|
||||||
|
|
||||||
"mi_configuration" = "PHP Configuration";
|
"mi_configuration" = "PHP Configuration";
|
||||||
@ -67,7 +71,11 @@
|
|||||||
"site_list.title" = "Domains";
|
"site_list.title" = "Domains";
|
||||||
"site_list.subtitle" = "Linked & Parked";
|
"site_list.subtitle" = "Linked & Parked";
|
||||||
|
|
||||||
"site_list.alerts_status_not_changed.title" = "SSL Status Not Changed";
|
"site_list.alerts_isolation_failed.title" = "Oops! Site Not Isolated";
|
||||||
|
"site_list.alerts_isolation_failed.subtitle" = "Something went wrong trying to isolate this site. If this is your default site but it is not linked, I recommend manually linking the site prior to setting up isolation.";
|
||||||
|
"site_list.alerts_isolation_failed.desc" = "To find out what goes wrong, you can try running the command in your terminal manually: %@";
|
||||||
|
|
||||||
|
"site_list.alerts_status_not_changed.title" = "Oops! SSL Status Not Changed";
|
||||||
"site_list.alerts_status_not_changed.desc" = "Something went wrong. Try running the command in your terminal manually: %@";
|
"site_list.alerts_status_not_changed.desc" = "Something went wrong. Try running the command in your terminal manually: %@";
|
||||||
|
|
||||||
"site_list.alerts_status_changed.title" = "SSL Status Changed";
|
"site_list.alerts_status_changed.title" = "SSL Status Changed";
|
||||||
@ -103,6 +111,10 @@
|
|||||||
|
|
||||||
// SITE LIST ACTIONS
|
// SITE LIST ACTIONS
|
||||||
|
|
||||||
|
"site_list.isolate" = "Isolate Domain";
|
||||||
|
"site_list.remove_isolation" = "Remove Isolation";
|
||||||
|
"site_list.isolation_unavailable" = "Isolation Not Supported (in Valet 2)";
|
||||||
|
|
||||||
"site_list.unlink" = "Unlink Directory";
|
"site_list.unlink" = "Unlink Directory";
|
||||||
"site_list.secure" = "Secure Domain";
|
"site_list.secure" = "Secure Domain";
|
||||||
"site_list.unsecure" = "Unsecure Domain";
|
"site_list.unsecure" = "Unsecure Domain";
|
||||||
@ -117,6 +129,12 @@
|
|||||||
"site_list.alert.invalid_folder_name" = "Invalid folder name";
|
"site_list.alert.invalid_folder_name" = "Invalid folder name";
|
||||||
"site_list.alert.invalid_folder_name_desc" = "This folder could not be resolved to a valid URL. This is usually because there’s a space in the folder name. Please rename the folder, reload the list of sites, and try again.";
|
"site_list.alert.invalid_folder_name_desc" = "This folder could not be resolved to a valid URL. This is usually because there’s a space in the folder name. Please rename the folder, reload the list of sites, and try again.";
|
||||||
|
|
||||||
|
"site_list.columns.tls" = "TLS";
|
||||||
|
"site_list.columns.domain" = "Domain";
|
||||||
|
"site_list.columns.php" = "PHP";
|
||||||
|
"site_list.columns.type" = "Type";
|
||||||
|
"site_list.columns.kind" = "Kind";
|
||||||
|
|
||||||
// DRIVERS
|
// DRIVERS
|
||||||
|
|
||||||
"driver.not_detected" = "Other";
|
"driver.not_detected" = "Other";
|
||||||
@ -202,8 +220,9 @@ problem manually, using your own Terminal app (this just shows you the output)."
|
|||||||
"alert.composer_success.info" = "Your global Composer dependencies have been successfully updated.";
|
"alert.composer_success.info" = "Your global Composer dependencies have been successfully updated.";
|
||||||
|
|
||||||
// Composer Version
|
// Composer Version
|
||||||
|
"alert.composer_php_isolated.desc" = "This site has been isolated, which means that Valet serves PHP %@ for this site specifically (the global version is %@).";
|
||||||
"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: %@.";
|
"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: %@.";
|
||||||
"alert.composer_php_requirement.type.unknown" = "The required PHP version was determined by an unknown factor.";
|
"alert.composer_php_requirement.type.unknown" = "The required PHP version is a mystery.";
|
||||||
"alert.composer_php_requirement.type.require" = "This required PHP version was determined by checking the `require` field in the `composer.json` file when the site list was last refreshed.";
|
"alert.composer_php_requirement.type.require" = "This required PHP version was determined by checking the `require` field in the `composer.json` file when the site list was last refreshed.";
|
||||||
"alert.composer_php_requirement.type.platform" = "This required PHP version was determined by checking the `platform` field in the `composer.json` file when the site list was last refreshed.";
|
"alert.composer_php_requirement.type.platform" = "This required PHP version was determined by checking the `platform` field in the `composer.json` file when the site list was last refreshed.";
|
||||||
"alert.composer_php_requirement.type.valetphprc" = "This required PHP version was determined by checking the .valetphprc file in your project's directory.";
|
"alert.composer_php_requirement.type.valetphprc" = "This required PHP version was determined by checking the .valetphprc file in your project's directory.";
|
||||||
@ -288,9 +307,10 @@ You can do this by running `composer global update` in your terminal. After that
|
|||||||
"startup.errors.valet_executable.subtitle" = "You must install Valet with Composer. The app will not work correctly until you resolve this issue.";
|
"startup.errors.valet_executable.subtitle" = "You must install Valet with Composer. The app will not work correctly until you resolve this issue.";
|
||||||
"startup.errors.valet_executable.desc" = "If you haven't installed Laravel Valet yet, please do so first. If you have it installed but are seeing this message anyway, then try running `which valet` in Terminal, it should return: `%@`.";
|
"startup.errors.valet_executable.desc" = "If you haven't installed Laravel Valet yet, please do so first. If you have it installed but are seeing this message anyway, then try running `which valet` in Terminal, it should return: `%@`.";
|
||||||
|
|
||||||
/// Valet configuration file missing [currently not enabled]
|
/// Valet configuration file missing or broken
|
||||||
"startup.errors.valet_config.title" = "Laravel Valet configuration file missing";
|
"startup.errors.valet_json_invalid.title" = "Laravel Valet configuration file invalid or missing";
|
||||||
"startup.errors.valet_config.desc" = "PHP Monitor needs to be able to read the configuration file in `~/.config/valet/config.json`.";
|
"startup.errors.valet_json_invalid.subtitle" = "PHP Monitor needs to be able to read the configuration file. It appears the file is malformed or missing. Please check that it exists and is formatted correctly.";
|
||||||
|
"startup.errors.valet_json_invalid.desc" = "You can find the file at `~/.config/valet/config.json`. If Laravel Valet cannot parse the configuration file, running any `valet` command will usually automatically fix the JSON file. Try running `valet --version` to automatically fix the file.";
|
||||||
|
|
||||||
/// Valet version not readable
|
/// Valet version not readable
|
||||||
"startup.errors.valet_version_unknown.title" = "Your Valet version could not be read";
|
"startup.errors.valet_version_unknown.title" = "Your Valet version could not be read";
|
||||||
@ -310,10 +330,6 @@ You can do this by running `composer global update` in your terminal. After that
|
|||||||
"startup.errors.services_json_error.subtitle" = "PHP Monitor usually queries `brew` using the following command to test if the services can be retrieved: `sudo brew services info nginx --json`.\n\nPHP Monitor could not interpret this response.";
|
"startup.errors.services_json_error.subtitle" = "PHP Monitor usually queries `brew` using the following command to test if the services can be retrieved: `sudo brew services info nginx --json`.\n\nPHP Monitor could not interpret this response.";
|
||||||
"startup.errors.services_json_error.desc" = "This can happen if your Homebrew installation is out of date, in which case Homebrew won't return JSON yet. You can usually fix this by running `brew update`. You can also try running `sudo brew services info nginx --json` in your terminal of choice.";
|
"startup.errors.services_json_error.desc" = "This can happen if your Homebrew installation is out of date, in which case Homebrew won't return JSON yet. You can usually fix this by running `brew update`. You can also try running `sudo brew services info nginx --json` in your terminal of choice.";
|
||||||
|
|
||||||
/// Multiple services active
|
|
||||||
"startup.errors.services.title" = "Multiple PHP services are active";
|
|
||||||
"startup.errors.services.desc" = "This can cause php-fpm to serve a more recent version of PHP than the one you'd like to see active. Please terminate all extra PHP processes.\n\nThe easiest solution is to choose the option 'First Aid & Services > Fix My Valet' in the menu bar.\n\nAlternatively, you can fix this manually. You can do this by running `brew services list` and running `sudo brew services stop php@7.3` (and use the version that applies).\n\nPHP Monitor usually handles the starting and stopping of these services, so once the correct version is the only PHP version running you should not have any issues. It is recommended to restart PHP Monitor once you have resolved this issue.\n\nFor more information about this issue, please see the README.md file in the repository on GitHub.";
|
|
||||||
|
|
||||||
// SPONSOR ENCOURAGEMENT
|
// SPONSOR ENCOURAGEMENT
|
||||||
|
|
||||||
"startup.sponsor_encouragement.title" = "If PHP Monitor has been useful to you or your company, please consider leaving a tip.";
|
"startup.sponsor_encouragement.title" = "If PHP Monitor has been useful to you or your company, please consider leaving a tip.";
|
||||||
|
50
phpmon/Vendor/HotKey/HotKey.swift
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import AppKit
|
||||||
|
import Carbon
|
||||||
|
|
||||||
|
public final class HotKey {
|
||||||
|
|
||||||
|
// MARK: - Types
|
||||||
|
|
||||||
|
public typealias Handler = () -> Void
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
let identifier = UUID()
|
||||||
|
|
||||||
|
public let keyCombo: KeyCombo
|
||||||
|
public var keyDownHandler: Handler?
|
||||||
|
public var keyUpHandler: Handler?
|
||||||
|
public var isPaused = false {
|
||||||
|
didSet {
|
||||||
|
if isPaused {
|
||||||
|
HotKeysController.unregister(self)
|
||||||
|
} else {
|
||||||
|
HotKeysController.register(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
|
||||||
|
public init(keyCombo: KeyCombo, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) {
|
||||||
|
self.keyCombo = keyCombo
|
||||||
|
self.keyDownHandler = keyDownHandler
|
||||||
|
self.keyUpHandler = keyUpHandler
|
||||||
|
|
||||||
|
HotKeysController.register(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(carbonKeyCode: UInt32, carbonModifiers: UInt32, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) {
|
||||||
|
let keyCombo = KeyCombo(carbonKeyCode: carbonKeyCode, carbonModifiers: carbonModifiers)
|
||||||
|
self.init(keyCombo: keyCombo, keyDownHandler: keyDownHandler, keyUpHandler: keyUpHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(key: Key, modifiers: NSEvent.ModifierFlags, keyDownHandler: Handler? = nil, keyUpHandler: Handler? = nil) {
|
||||||
|
let keyCombo = KeyCombo(key: key, modifiers: modifiers)
|
||||||
|
self.init(keyCombo: keyCombo, keyDownHandler: keyDownHandler, keyUpHandler: keyUpHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
HotKeysController.unregister(self)
|
||||||
|
}
|
||||||
|
}
|
185
phpmon/Vendor/HotKey/HotKeysController.swift
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import Carbon
|
||||||
|
|
||||||
|
final class HotKeysController {
|
||||||
|
|
||||||
|
// MARK: - Types
|
||||||
|
|
||||||
|
final class HotKeyBox {
|
||||||
|
let identifier: UUID
|
||||||
|
weak var hotKey: HotKey?
|
||||||
|
let carbonHotKeyID: UInt32
|
||||||
|
var carbonEventHotKey: EventHotKeyRef?
|
||||||
|
|
||||||
|
init(hotKey: HotKey, carbonHotKeyID: UInt32) {
|
||||||
|
self.identifier = hotKey.identifier
|
||||||
|
self.hotKey = hotKey
|
||||||
|
self.carbonHotKeyID = carbonHotKeyID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
static var hotKeys = [UInt32: HotKeyBox]()
|
||||||
|
static private var hotKeysCount: UInt32 = 0
|
||||||
|
|
||||||
|
static let eventHotKeySignature: UInt32 = {
|
||||||
|
let string = "SSHk"
|
||||||
|
var result: FourCharCode = 0
|
||||||
|
for char in string.utf16 {
|
||||||
|
result = (result << 8) + FourCharCode(char)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
private static let eventSpec = [
|
||||||
|
EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)),
|
||||||
|
EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyReleased))
|
||||||
|
]
|
||||||
|
|
||||||
|
private static var eventHandler: EventHandlerRef?
|
||||||
|
|
||||||
|
// MARK: - Registration
|
||||||
|
|
||||||
|
static func register(_ hotKey: HotKey) {
|
||||||
|
// Don't register an already registered HotKey
|
||||||
|
if hotKeys.values.first(where: { $0.identifier == hotKey.identifier }) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the count which will become out next ID
|
||||||
|
hotKeysCount += 1
|
||||||
|
|
||||||
|
// Create a box for our metadata and weak HotKey
|
||||||
|
let box = HotKeyBox(hotKey: hotKey, carbonHotKeyID: hotKeysCount)
|
||||||
|
hotKeys[box.carbonHotKeyID] = box
|
||||||
|
|
||||||
|
// Register with the system
|
||||||
|
var eventHotKey: EventHotKeyRef?
|
||||||
|
let hotKeyID = EventHotKeyID(signature: eventHotKeySignature, id: box.carbonHotKeyID)
|
||||||
|
let registerError = RegisterEventHotKey(
|
||||||
|
hotKey.keyCombo.carbonKeyCode,
|
||||||
|
hotKey.keyCombo.carbonModifiers,
|
||||||
|
hotKeyID,
|
||||||
|
GetEventDispatcherTarget(),
|
||||||
|
0,
|
||||||
|
&eventHotKey
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure registration worked
|
||||||
|
guard registerError == noErr, eventHotKey != nil else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the event so we can unregister it later
|
||||||
|
box.carbonEventHotKey = eventHotKey
|
||||||
|
|
||||||
|
// Setup the event handler if needed
|
||||||
|
updateEventHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func unregister(_ hotKey: HotKey) {
|
||||||
|
// Find the box
|
||||||
|
guard let box = self.box(for: hotKey) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister the hot key
|
||||||
|
UnregisterEventHotKey(box.carbonEventHotKey)
|
||||||
|
|
||||||
|
// Destroy the box
|
||||||
|
box.hotKey = nil
|
||||||
|
hotKeys.removeValue(forKey: box.carbonHotKeyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Events
|
||||||
|
|
||||||
|
static func handleCarbonEvent(_ event: EventRef?) -> OSStatus {
|
||||||
|
// Ensure we have an event
|
||||||
|
guard let event = event else {
|
||||||
|
return OSStatus(eventNotHandledErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the hot key ID from the event
|
||||||
|
var hotKeyID = EventHotKeyID()
|
||||||
|
let error = GetEventParameter(
|
||||||
|
event,
|
||||||
|
UInt32(kEventParamDirectObject),
|
||||||
|
UInt32(typeEventHotKeyID),
|
||||||
|
nil,
|
||||||
|
MemoryLayout<EventHotKeyID>.size,
|
||||||
|
nil,
|
||||||
|
&hotKeyID
|
||||||
|
)
|
||||||
|
|
||||||
|
if error != noErr {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have a HotKey registered for this ID
|
||||||
|
guard hotKeyID.signature == eventHotKeySignature,
|
||||||
|
let hotKey = self.hotKey(for: hotKeyID.id)
|
||||||
|
else {
|
||||||
|
return OSStatus(eventNotHandledErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the handler
|
||||||
|
switch GetEventKind(event) {
|
||||||
|
case UInt32(kEventHotKeyPressed):
|
||||||
|
if !hotKey.isPaused, let handler = hotKey.keyDownHandler {
|
||||||
|
handler()
|
||||||
|
return noErr
|
||||||
|
}
|
||||||
|
case UInt32(kEventHotKeyReleased):
|
||||||
|
if !hotKey.isPaused, let handler = hotKey.keyUpHandler {
|
||||||
|
handler()
|
||||||
|
return noErr
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return OSStatus(eventNotHandledErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func updateEventHandler() {
|
||||||
|
if hotKeysCount == 0 || eventHandler != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register for key down and key up
|
||||||
|
let eventSpec = [
|
||||||
|
EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyPressed)),
|
||||||
|
EventTypeSpec(eventClass: OSType(kEventClassKeyboard), eventKind: UInt32(kEventHotKeyReleased))
|
||||||
|
]
|
||||||
|
|
||||||
|
// Install the handler
|
||||||
|
InstallEventHandler(GetEventDispatcherTarget(), hotKeyEventHandler, 2, eventSpec, nil, &eventHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Querying
|
||||||
|
|
||||||
|
private static func hotKey(for carbonHotKeyID: UInt32) -> HotKey? {
|
||||||
|
if let hotKey = hotKeys[carbonHotKeyID]?.hotKey {
|
||||||
|
return hotKey
|
||||||
|
}
|
||||||
|
|
||||||
|
hotKeys.removeValue(forKey: carbonHotKeyID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func box(for hotKey: HotKey) -> HotKeyBox? {
|
||||||
|
for box in hotKeys.values {
|
||||||
|
if box.identifier == hotKey.identifier {
|
||||||
|
return box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func hotKeyEventHandler(eventHandlerCall: EventHandlerCallRef?, event: EventRef?, userData: UnsafeMutableRawPointer?) -> OSStatus {
|
||||||
|
return HotKeysController.handleCarbonEvent(event)
|
||||||
|
}
|
497
phpmon/Vendor/HotKey/Key.swift
vendored
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
import Carbon
|
||||||
|
|
||||||
|
public enum Key {
|
||||||
|
|
||||||
|
// MARK: - Letters
|
||||||
|
case a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
|
||||||
|
|
||||||
|
// MARK: - Numbers
|
||||||
|
case zero, one, two, three, four, five, six, seven, eight, nine
|
||||||
|
|
||||||
|
// MARK: - Symbols
|
||||||
|
case period, quote, rightBracket, semicolon, slash, backslash, comma, equal, grave, leftBracket, minus
|
||||||
|
|
||||||
|
// MARK: - Whitespace
|
||||||
|
case space, tab, `return`
|
||||||
|
|
||||||
|
// MARK: - Modifiers
|
||||||
|
case command, rightCommand, option, rightOption, control, rightControl, shift, rightShift, function, capsLock
|
||||||
|
|
||||||
|
// MARK: - Navigation
|
||||||
|
case pageUp, pageDown, home, end, upArrow, rightArrow, downArrow, leftArrow
|
||||||
|
|
||||||
|
// MARK: - Functions
|
||||||
|
case f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20
|
||||||
|
|
||||||
|
// MARK: - Keypad
|
||||||
|
case keypad0, keypad1, keypad2, keypad3, keypad4, keypad5, keypad6, keypad7, keypad8, keypad9,
|
||||||
|
keypadClear, keypadDecimal, keypadDivide, keypadEnter, keypadEquals, keypadMinus, keypadMultiply, keypadPlus
|
||||||
|
|
||||||
|
// MARK: - Misc
|
||||||
|
case escape, delete, forwardDelete, help, volumeUp, volumeDown, mute
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
|
||||||
|
public init?(string: String) {
|
||||||
|
switch string.lowercased() {
|
||||||
|
case "a": self = .a
|
||||||
|
case "s": self = .s
|
||||||
|
case "d": self = .d
|
||||||
|
case "f": self = .f
|
||||||
|
case "h": self = .h
|
||||||
|
case "g": self = .g
|
||||||
|
case "z": self = .z
|
||||||
|
case "x": self = .x
|
||||||
|
case "c": self = .c
|
||||||
|
case "v": self = .v
|
||||||
|
case "b": self = .b
|
||||||
|
case "q": self = .q
|
||||||
|
case "w": self = .w
|
||||||
|
case "e": self = .e
|
||||||
|
case "r": self = .r
|
||||||
|
case "y": self = .y
|
||||||
|
case "t": self = .t
|
||||||
|
case "one", "1": self = .one
|
||||||
|
case "two", "2": self = .two
|
||||||
|
case "three", "3": self = .three
|
||||||
|
case "four", "4": self = .four
|
||||||
|
case "six", "6": self = .six
|
||||||
|
case "five", "5": self = .five
|
||||||
|
case "equal", "=": self = .equal
|
||||||
|
case "nine", "9": self = .nine
|
||||||
|
case "seven", "7": self = .seven
|
||||||
|
case "minus", "-": self = .minus
|
||||||
|
case "eight", "8": self = .eight
|
||||||
|
case "zero", "0": self = .zero
|
||||||
|
case "rightBracket", "]": self = .rightBracket
|
||||||
|
case "o": self = .o
|
||||||
|
case "u": self = .u
|
||||||
|
case "leftBracket", "[": self = .leftBracket
|
||||||
|
case "i": self = .i
|
||||||
|
case "p": self = .p
|
||||||
|
case "l": self = .l
|
||||||
|
case "j": self = .j
|
||||||
|
case "quote", "\"": self = .quote
|
||||||
|
case "k": self = .k
|
||||||
|
case "semicolon", ";": self = .semicolon
|
||||||
|
case "backslash", "\\": self = .backslash
|
||||||
|
case "comma", ",": self = .comma
|
||||||
|
case "slash", "/": self = .slash
|
||||||
|
case "n": self = .n
|
||||||
|
case "m": self = .m
|
||||||
|
case "period", ".": self = .period
|
||||||
|
case "grave", "`", "ˋ", "`": self = .grave
|
||||||
|
case "keypaddecimal": self = .keypadDecimal
|
||||||
|
case "keypadmultiply": self = .keypadMultiply
|
||||||
|
case "keypadplus": self = .keypadPlus
|
||||||
|
case "keypadclear", "⌧": self = .keypadClear
|
||||||
|
case "keypaddivide": self = .keypadDivide
|
||||||
|
case "keypadenter": self = .keypadEnter
|
||||||
|
case "keypadminus": self = .keypadMinus
|
||||||
|
case "keypadequals": self = .keypadEquals
|
||||||
|
case "keypad0": self = .keypad0
|
||||||
|
case "keypad1": self = .keypad1
|
||||||
|
case "keypad2": self = .keypad2
|
||||||
|
case "keypad3": self = .keypad3
|
||||||
|
case "keypad4": self = .keypad4
|
||||||
|
case "keypad5": self = .keypad5
|
||||||
|
case "keypad6": self = .keypad6
|
||||||
|
case "keypad7": self = .keypad7
|
||||||
|
case "keypad8": self = .keypad8
|
||||||
|
case "keypad9": self = .keypad9
|
||||||
|
case "return", "\r", "↩︎", "⏎", "⮐": self = .return
|
||||||
|
case "tab", "\t", "⇥": self = .tab
|
||||||
|
case "space", " ", "␣": self = .space
|
||||||
|
case "delete", "⌫": self = .delete
|
||||||
|
case "escape", "⎋": self = .escape
|
||||||
|
case "command", "⌘", "": self = .command
|
||||||
|
case "shift", "⇧": self = .shift
|
||||||
|
case "capslock", "⇪": self = .capsLock
|
||||||
|
case "option", "⌥": self = .option
|
||||||
|
case "control", "⌃": self = .control
|
||||||
|
case "rightcommand": self = .rightCommand
|
||||||
|
case "rightshift": self = .rightShift
|
||||||
|
case "rightoption": self = .rightOption
|
||||||
|
case "rightcontrol": self = .rightControl
|
||||||
|
case "function", "fn": self = .function
|
||||||
|
case "f17", "F17": self = .f17
|
||||||
|
case "volumeup", "🔊": self = .volumeUp
|
||||||
|
case "volumedown", "🔉": self = .volumeDown
|
||||||
|
case "mute", "🔇": self = .mute
|
||||||
|
case "f18", "F18": self = .f18
|
||||||
|
case "f19", "F19": self = .f19
|
||||||
|
case "f20", "F20": self = .f20
|
||||||
|
case "f5", "F5": self = .f5
|
||||||
|
case "f6", "F6": self = .f6
|
||||||
|
case "f7", "F7": self = .f7
|
||||||
|
case "f3", "F3": self = .f3
|
||||||
|
case "f8", "F8": self = .f8
|
||||||
|
case "f9", "F9": self = .f9
|
||||||
|
case "f11", "F11": self = .f11
|
||||||
|
case "f13", "F13": self = .f13
|
||||||
|
case "f16", "F16": self = .f16
|
||||||
|
case "f14", "F14": self = .f14
|
||||||
|
case "f10", "F10": self = .f10
|
||||||
|
case "f12", "F12": self = .f12
|
||||||
|
case "f15", "F15": self = .f15
|
||||||
|
case "help", "?⃝": self = .help
|
||||||
|
case "home", "↖": self = .home
|
||||||
|
case "pageup", "⇞": self = .pageUp
|
||||||
|
case "forwarddelete", "⌦": self = .forwardDelete
|
||||||
|
case "f4", "F4": self = .f4
|
||||||
|
case "end", "↘": self = .end
|
||||||
|
case "f2", "F2": self = .f2
|
||||||
|
case "pagedown", "⇟": self = .pageDown
|
||||||
|
case "f1", "F1": self = .f1
|
||||||
|
case "leftarrow", "←": self = .leftArrow
|
||||||
|
case "rightarrow", "→": self = .rightArrow
|
||||||
|
case "downarrow", "↓": self = .downArrow
|
||||||
|
case "uparrow", "↑": self = .upArrow
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init?(carbonKeyCode: UInt32) {
|
||||||
|
switch carbonKeyCode {
|
||||||
|
case UInt32(kVK_ANSI_A): self = .a
|
||||||
|
case UInt32(kVK_ANSI_S): self = .s
|
||||||
|
case UInt32(kVK_ANSI_D): self = .d
|
||||||
|
case UInt32(kVK_ANSI_F): self = .f
|
||||||
|
case UInt32(kVK_ANSI_H): self = .h
|
||||||
|
case UInt32(kVK_ANSI_G): self = .g
|
||||||
|
case UInt32(kVK_ANSI_Z): self = .z
|
||||||
|
case UInt32(kVK_ANSI_X): self = .x
|
||||||
|
case UInt32(kVK_ANSI_C): self = .c
|
||||||
|
case UInt32(kVK_ANSI_V): self = .v
|
||||||
|
case UInt32(kVK_ANSI_B): self = .b
|
||||||
|
case UInt32(kVK_ANSI_Q): self = .q
|
||||||
|
case UInt32(kVK_ANSI_W): self = .w
|
||||||
|
case UInt32(kVK_ANSI_E): self = .e
|
||||||
|
case UInt32(kVK_ANSI_R): self = .r
|
||||||
|
case UInt32(kVK_ANSI_Y): self = .y
|
||||||
|
case UInt32(kVK_ANSI_T): self = .t
|
||||||
|
case UInt32(kVK_ANSI_1): self = .one
|
||||||
|
case UInt32(kVK_ANSI_2): self = .two
|
||||||
|
case UInt32(kVK_ANSI_3): self = .three
|
||||||
|
case UInt32(kVK_ANSI_4): self = .four
|
||||||
|
case UInt32(kVK_ANSI_6): self = .six
|
||||||
|
case UInt32(kVK_ANSI_5): self = .five
|
||||||
|
case UInt32(kVK_ANSI_Equal): self = .equal
|
||||||
|
case UInt32(kVK_ANSI_9): self = .nine
|
||||||
|
case UInt32(kVK_ANSI_7): self = .seven
|
||||||
|
case UInt32(kVK_ANSI_Minus): self = .minus
|
||||||
|
case UInt32(kVK_ANSI_8): self = .eight
|
||||||
|
case UInt32(kVK_ANSI_0): self = .zero
|
||||||
|
case UInt32(kVK_ANSI_RightBracket): self = .rightBracket
|
||||||
|
case UInt32(kVK_ANSI_O): self = .o
|
||||||
|
case UInt32(kVK_ANSI_U): self = .u
|
||||||
|
case UInt32(kVK_ANSI_LeftBracket): self = .leftBracket
|
||||||
|
case UInt32(kVK_ANSI_I): self = .i
|
||||||
|
case UInt32(kVK_ANSI_P): self = .p
|
||||||
|
case UInt32(kVK_ANSI_L): self = .l
|
||||||
|
case UInt32(kVK_ANSI_J): self = .j
|
||||||
|
case UInt32(kVK_ANSI_Quote): self = .quote
|
||||||
|
case UInt32(kVK_ANSI_K): self = .k
|
||||||
|
case UInt32(kVK_ANSI_Semicolon): self = .semicolon
|
||||||
|
case UInt32(kVK_ANSI_Backslash): self = .backslash
|
||||||
|
case UInt32(kVK_ANSI_Comma): self = .comma
|
||||||
|
case UInt32(kVK_ANSI_Slash): self = .slash
|
||||||
|
case UInt32(kVK_ANSI_N): self = .n
|
||||||
|
case UInt32(kVK_ANSI_M): self = .m
|
||||||
|
case UInt32(kVK_ANSI_Period): self = .period
|
||||||
|
case UInt32(kVK_ANSI_Grave): self = .grave
|
||||||
|
case UInt32(kVK_ANSI_KeypadDecimal): self = .keypadDecimal
|
||||||
|
case UInt32(kVK_ANSI_KeypadMultiply): self = .keypadMultiply
|
||||||
|
case UInt32(kVK_ANSI_KeypadPlus): self = .keypadPlus
|
||||||
|
case UInt32(kVK_ANSI_KeypadClear): self = .keypadClear
|
||||||
|
case UInt32(kVK_ANSI_KeypadDivide): self = .keypadDivide
|
||||||
|
case UInt32(kVK_ANSI_KeypadEnter): self = .keypadEnter
|
||||||
|
case UInt32(kVK_ANSI_KeypadMinus): self = .keypadMinus
|
||||||
|
case UInt32(kVK_ANSI_KeypadEquals): self = .keypadEquals
|
||||||
|
case UInt32(kVK_ANSI_Keypad0): self = .keypad0
|
||||||
|
case UInt32(kVK_ANSI_Keypad1): self = .keypad1
|
||||||
|
case UInt32(kVK_ANSI_Keypad2): self = .keypad2
|
||||||
|
case UInt32(kVK_ANSI_Keypad3): self = .keypad3
|
||||||
|
case UInt32(kVK_ANSI_Keypad4): self = .keypad4
|
||||||
|
case UInt32(kVK_ANSI_Keypad5): self = .keypad5
|
||||||
|
case UInt32(kVK_ANSI_Keypad6): self = .keypad6
|
||||||
|
case UInt32(kVK_ANSI_Keypad7): self = .keypad7
|
||||||
|
case UInt32(kVK_ANSI_Keypad8): self = .keypad8
|
||||||
|
case UInt32(kVK_ANSI_Keypad9): self = .keypad9
|
||||||
|
case UInt32(kVK_Return): self = .`return`
|
||||||
|
case UInt32(kVK_Tab): self = .tab
|
||||||
|
case UInt32(kVK_Space): self = .space
|
||||||
|
case UInt32(kVK_Delete): self = .delete
|
||||||
|
case UInt32(kVK_Escape): self = .escape
|
||||||
|
case UInt32(kVK_Command): self = .command
|
||||||
|
case UInt32(kVK_Shift): self = .shift
|
||||||
|
case UInt32(kVK_CapsLock): self = .capsLock
|
||||||
|
case UInt32(kVK_Option): self = .option
|
||||||
|
case UInt32(kVK_Control): self = .control
|
||||||
|
case UInt32(kVK_RightCommand): self = .rightCommand
|
||||||
|
case UInt32(kVK_RightShift): self = .rightShift
|
||||||
|
case UInt32(kVK_RightOption): self = .rightOption
|
||||||
|
case UInt32(kVK_RightControl): self = .rightControl
|
||||||
|
case UInt32(kVK_Function): self = .function
|
||||||
|
case UInt32(kVK_F17): self = .f17
|
||||||
|
case UInt32(kVK_VolumeUp): self = .volumeUp
|
||||||
|
case UInt32(kVK_VolumeDown): self = .volumeDown
|
||||||
|
case UInt32(kVK_Mute): self = .mute
|
||||||
|
case UInt32(kVK_F18): self = .f18
|
||||||
|
case UInt32(kVK_F19): self = .f19
|
||||||
|
case UInt32(kVK_F20): self = .f20
|
||||||
|
case UInt32(kVK_F5): self = .f5
|
||||||
|
case UInt32(kVK_F6): self = .f6
|
||||||
|
case UInt32(kVK_F7): self = .f7
|
||||||
|
case UInt32(kVK_F3): self = .f3
|
||||||
|
case UInt32(kVK_F8): self = .f8
|
||||||
|
case UInt32(kVK_F9): self = .f9
|
||||||
|
case UInt32(kVK_F11): self = .f11
|
||||||
|
case UInt32(kVK_F13): self = .f13
|
||||||
|
case UInt32(kVK_F16): self = .f16
|
||||||
|
case UInt32(kVK_F14): self = .f14
|
||||||
|
case UInt32(kVK_F10): self = .f10
|
||||||
|
case UInt32(kVK_F12): self = .f12
|
||||||
|
case UInt32(kVK_F15): self = .f15
|
||||||
|
case UInt32(kVK_Help): self = .help
|
||||||
|
case UInt32(kVK_Home): self = .home
|
||||||
|
case UInt32(kVK_PageUp): self = .pageUp
|
||||||
|
case UInt32(kVK_ForwardDelete): self = .forwardDelete
|
||||||
|
case UInt32(kVK_F4): self = .f4
|
||||||
|
case UInt32(kVK_End): self = .end
|
||||||
|
case UInt32(kVK_F2): self = .f2
|
||||||
|
case UInt32(kVK_PageDown): self = .pageDown
|
||||||
|
case UInt32(kVK_F1): self = .f1
|
||||||
|
case UInt32(kVK_LeftArrow): self = .leftArrow
|
||||||
|
case UInt32(kVK_RightArrow): self = .rightArrow
|
||||||
|
case UInt32(kVK_DownArrow): self = .downArrow
|
||||||
|
case UInt32(kVK_UpArrow): self = .upArrow
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var carbonKeyCode: UInt32 {
|
||||||
|
switch self {
|
||||||
|
case .a: return UInt32(kVK_ANSI_A)
|
||||||
|
case .s: return UInt32(kVK_ANSI_S)
|
||||||
|
case .d: return UInt32(kVK_ANSI_D)
|
||||||
|
case .f: return UInt32(kVK_ANSI_F)
|
||||||
|
case .h: return UInt32(kVK_ANSI_H)
|
||||||
|
case .g: return UInt32(kVK_ANSI_G)
|
||||||
|
case .z: return UInt32(kVK_ANSI_Z)
|
||||||
|
case .x: return UInt32(kVK_ANSI_X)
|
||||||
|
case .c: return UInt32(kVK_ANSI_C)
|
||||||
|
case .v: return UInt32(kVK_ANSI_V)
|
||||||
|
case .b: return UInt32(kVK_ANSI_B)
|
||||||
|
case .q: return UInt32(kVK_ANSI_Q)
|
||||||
|
case .w: return UInt32(kVK_ANSI_W)
|
||||||
|
case .e: return UInt32(kVK_ANSI_E)
|
||||||
|
case .r: return UInt32(kVK_ANSI_R)
|
||||||
|
case .y: return UInt32(kVK_ANSI_Y)
|
||||||
|
case .t: return UInt32(kVK_ANSI_T)
|
||||||
|
case .one: return UInt32(kVK_ANSI_1)
|
||||||
|
case .two: return UInt32(kVK_ANSI_2)
|
||||||
|
case .three: return UInt32(kVK_ANSI_3)
|
||||||
|
case .four: return UInt32(kVK_ANSI_4)
|
||||||
|
case .six: return UInt32(kVK_ANSI_6)
|
||||||
|
case .five: return UInt32(kVK_ANSI_5)
|
||||||
|
case .equal: return UInt32(kVK_ANSI_Equal)
|
||||||
|
case .nine: return UInt32(kVK_ANSI_9)
|
||||||
|
case .seven: return UInt32(kVK_ANSI_7)
|
||||||
|
case .minus: return UInt32(kVK_ANSI_Minus)
|
||||||
|
case .eight: return UInt32(kVK_ANSI_8)
|
||||||
|
case .zero: return UInt32(kVK_ANSI_0)
|
||||||
|
case .rightBracket: return UInt32(kVK_ANSI_RightBracket)
|
||||||
|
case .o: return UInt32(kVK_ANSI_O)
|
||||||
|
case .u: return UInt32(kVK_ANSI_U)
|
||||||
|
case .leftBracket: return UInt32(kVK_ANSI_LeftBracket)
|
||||||
|
case .i: return UInt32(kVK_ANSI_I)
|
||||||
|
case .p: return UInt32(kVK_ANSI_P)
|
||||||
|
case .l: return UInt32(kVK_ANSI_L)
|
||||||
|
case .j: return UInt32(kVK_ANSI_J)
|
||||||
|
case .quote: return UInt32(kVK_ANSI_Quote)
|
||||||
|
case .k: return UInt32(kVK_ANSI_K)
|
||||||
|
case .semicolon: return UInt32(kVK_ANSI_Semicolon)
|
||||||
|
case .backslash: return UInt32(kVK_ANSI_Backslash)
|
||||||
|
case .comma: return UInt32(kVK_ANSI_Comma)
|
||||||
|
case .slash: return UInt32(kVK_ANSI_Slash)
|
||||||
|
case .n: return UInt32(kVK_ANSI_N)
|
||||||
|
case .m: return UInt32(kVK_ANSI_M)
|
||||||
|
case .period: return UInt32(kVK_ANSI_Period)
|
||||||
|
case .grave: return UInt32(kVK_ANSI_Grave)
|
||||||
|
case .keypadDecimal: return UInt32(kVK_ANSI_KeypadDecimal)
|
||||||
|
case .keypadMultiply: return UInt32(kVK_ANSI_KeypadMultiply)
|
||||||
|
case .keypadPlus: return UInt32(kVK_ANSI_KeypadPlus)
|
||||||
|
case .keypadClear: return UInt32(kVK_ANSI_KeypadClear)
|
||||||
|
case .keypadDivide: return UInt32(kVK_ANSI_KeypadDivide)
|
||||||
|
case .keypadEnter: return UInt32(kVK_ANSI_KeypadEnter)
|
||||||
|
case .keypadMinus: return UInt32(kVK_ANSI_KeypadMinus)
|
||||||
|
case .keypadEquals: return UInt32(kVK_ANSI_KeypadEquals)
|
||||||
|
case .keypad0: return UInt32(kVK_ANSI_Keypad0)
|
||||||
|
case .keypad1: return UInt32(kVK_ANSI_Keypad1)
|
||||||
|
case .keypad2: return UInt32(kVK_ANSI_Keypad2)
|
||||||
|
case .keypad3: return UInt32(kVK_ANSI_Keypad3)
|
||||||
|
case .keypad4: return UInt32(kVK_ANSI_Keypad4)
|
||||||
|
case .keypad5: return UInt32(kVK_ANSI_Keypad5)
|
||||||
|
case .keypad6: return UInt32(kVK_ANSI_Keypad6)
|
||||||
|
case .keypad7: return UInt32(kVK_ANSI_Keypad7)
|
||||||
|
case .keypad8: return UInt32(kVK_ANSI_Keypad8)
|
||||||
|
case .keypad9: return UInt32(kVK_ANSI_Keypad9)
|
||||||
|
case .`return`: return UInt32(kVK_Return)
|
||||||
|
case .tab: return UInt32(kVK_Tab)
|
||||||
|
case .space: return UInt32(kVK_Space)
|
||||||
|
case .delete: return UInt32(kVK_Delete)
|
||||||
|
case .escape: return UInt32(kVK_Escape)
|
||||||
|
case .command: return UInt32(kVK_Command)
|
||||||
|
case .shift: return UInt32(kVK_Shift)
|
||||||
|
case .capsLock: return UInt32(kVK_CapsLock)
|
||||||
|
case .option: return UInt32(kVK_Option)
|
||||||
|
case .control: return UInt32(kVK_Control)
|
||||||
|
case .rightCommand: return UInt32(kVK_RightCommand)
|
||||||
|
case .rightShift: return UInt32(kVK_RightShift)
|
||||||
|
case .rightOption: return UInt32(kVK_RightOption)
|
||||||
|
case .rightControl: return UInt32(kVK_RightControl)
|
||||||
|
case .function: return UInt32(kVK_Function)
|
||||||
|
case .f17: return UInt32(kVK_F17)
|
||||||
|
case .volumeUp: return UInt32(kVK_VolumeUp)
|
||||||
|
case .volumeDown: return UInt32(kVK_VolumeDown)
|
||||||
|
case .mute: return UInt32(kVK_Mute)
|
||||||
|
case .f18: return UInt32(kVK_F18)
|
||||||
|
case .f19: return UInt32(kVK_F19)
|
||||||
|
case .f20: return UInt32(kVK_F20)
|
||||||
|
case .f5: return UInt32(kVK_F5)
|
||||||
|
case .f6: return UInt32(kVK_F6)
|
||||||
|
case .f7: return UInt32(kVK_F7)
|
||||||
|
case .f3: return UInt32(kVK_F3)
|
||||||
|
case .f8: return UInt32(kVK_F8)
|
||||||
|
case .f9: return UInt32(kVK_F9)
|
||||||
|
case .f11: return UInt32(kVK_F11)
|
||||||
|
case .f13: return UInt32(kVK_F13)
|
||||||
|
case .f16: return UInt32(kVK_F16)
|
||||||
|
case .f14: return UInt32(kVK_F14)
|
||||||
|
case .f10: return UInt32(kVK_F10)
|
||||||
|
case .f12: return UInt32(kVK_F12)
|
||||||
|
case .f15: return UInt32(kVK_F15)
|
||||||
|
case .help: return UInt32(kVK_Help)
|
||||||
|
case .home: return UInt32(kVK_Home)
|
||||||
|
case .pageUp: return UInt32(kVK_PageUp)
|
||||||
|
case .forwardDelete: return UInt32(kVK_ForwardDelete)
|
||||||
|
case .f4: return UInt32(kVK_F4)
|
||||||
|
case .end: return UInt32(kVK_End)
|
||||||
|
case .f2: return UInt32(kVK_F2)
|
||||||
|
case .pageDown: return UInt32(kVK_PageDown)
|
||||||
|
case .f1: return UInt32(kVK_F1)
|
||||||
|
case .leftArrow: return UInt32(kVK_LeftArrow)
|
||||||
|
case .rightArrow: return UInt32(kVK_RightArrow)
|
||||||
|
case .downArrow: return UInt32(kVK_DownArrow)
|
||||||
|
case .upArrow: return UInt32(kVK_UpArrow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Key: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
switch self {
|
||||||
|
case .a: return "A"
|
||||||
|
case .s: return "S"
|
||||||
|
case .d: return "D"
|
||||||
|
case .f: return "F"
|
||||||
|
case .h: return "H"
|
||||||
|
case .g: return "G"
|
||||||
|
case .z: return "Z"
|
||||||
|
case .x: return "X"
|
||||||
|
case .c: return "C"
|
||||||
|
case .v: return "V"
|
||||||
|
case .b: return "B"
|
||||||
|
case .q: return "Q"
|
||||||
|
case .w: return "W"
|
||||||
|
case .e: return "E"
|
||||||
|
case .r: return "R"
|
||||||
|
case .y: return "Y"
|
||||||
|
case .t: return "T"
|
||||||
|
case .one, .keypad1: return "1"
|
||||||
|
case .two, .keypad2: return "2"
|
||||||
|
case .three, .keypad3: return "3"
|
||||||
|
case .four, .keypad4: return "4"
|
||||||
|
case .six, .keypad6: return "6"
|
||||||
|
case .five, .keypad5: return "5"
|
||||||
|
case .equal: return "="
|
||||||
|
case .nine, .keypad9: return "9"
|
||||||
|
case .seven, .keypad7: return "7"
|
||||||
|
case .minus: return "-"
|
||||||
|
case .eight, .keypad8: return "8"
|
||||||
|
case .zero, .keypad0: return "0"
|
||||||
|
case .rightBracket: return "]"
|
||||||
|
case .o: return "O"
|
||||||
|
case .u: return "U"
|
||||||
|
case .leftBracket: return "["
|
||||||
|
case .i: return "I"
|
||||||
|
case .p: return "P"
|
||||||
|
case .l: return "L"
|
||||||
|
case .j: return "J"
|
||||||
|
case .quote: return "\""
|
||||||
|
case .k: return "K"
|
||||||
|
case .semicolon: return ";"
|
||||||
|
case .backslash: return "\\"
|
||||||
|
case .comma: return ","
|
||||||
|
case .slash: return "/"
|
||||||
|
case .n: return "N"
|
||||||
|
case .m: return "M"
|
||||||
|
case .period: return "."
|
||||||
|
case .grave: return "`"
|
||||||
|
case .keypadDecimal: return "."
|
||||||
|
case .keypadMultiply: return "𝗑"
|
||||||
|
case .keypadPlus: return "+"
|
||||||
|
case .keypadClear: return "⌧"
|
||||||
|
case .keypadDivide: return "/"
|
||||||
|
case .keypadEnter: return "↩︎"
|
||||||
|
case .keypadMinus: return "-"
|
||||||
|
case .keypadEquals: return "="
|
||||||
|
case .`return`: return "↩︎"
|
||||||
|
case .tab: return "⇥"
|
||||||
|
case .space: return "␣"
|
||||||
|
case .delete: return "⌫"
|
||||||
|
case .escape: return "⎋"
|
||||||
|
case .command, .rightCommand: return "⌘"
|
||||||
|
case .shift, .rightShift: return "⇧"
|
||||||
|
case .capsLock: return "⇪"
|
||||||
|
case .option, .rightOption: return "⌥"
|
||||||
|
case .control, .rightControl: return "⌃"
|
||||||
|
case .function: return "fn"
|
||||||
|
case .f17: return "F17"
|
||||||
|
case .volumeUp: return "🔊"
|
||||||
|
case .volumeDown: return "🔉"
|
||||||
|
case .mute: return "🔇"
|
||||||
|
case .f18: return "F18"
|
||||||
|
case .f19: return "F19"
|
||||||
|
case .f20: return "F20"
|
||||||
|
case .f5: return "F5"
|
||||||
|
case .f6: return "F6"
|
||||||
|
case .f7: return "F7"
|
||||||
|
case .f3: return "F3"
|
||||||
|
case .f8: return "F8"
|
||||||
|
case .f9: return "F9"
|
||||||
|
case .f11: return "F11"
|
||||||
|
case .f13: return "F13"
|
||||||
|
case .f16: return "F16"
|
||||||
|
case .f14: return "F14"
|
||||||
|
case .f10: return "F10"
|
||||||
|
case .f12: return "F12"
|
||||||
|
case .f15: return "F15"
|
||||||
|
case .help: return "?⃝"
|
||||||
|
case .home: return "↖"
|
||||||
|
case .pageUp: return "⇞"
|
||||||
|
case .forwardDelete: return "⌦"
|
||||||
|
case .f4: return "F4"
|
||||||
|
case .end: return "↘"
|
||||||
|
case .f2: return "F2"
|
||||||
|
case .pageDown: return "⇟"
|
||||||
|
case .f1: return "F1"
|
||||||
|
case .leftArrow: return "←"
|
||||||
|
case .rightArrow: return "→"
|
||||||
|
case .downArrow: return "↓"
|
||||||
|
case .upArrow: return "↑"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
phpmon/Vendor/HotKey/KeyCombo.swift
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import AppKit
|
||||||
|
|
||||||
|
public struct KeyCombo: Equatable {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
public var carbonKeyCode: UInt32
|
||||||
|
public var carbonModifiers: UInt32
|
||||||
|
|
||||||
|
public var key: Key? {
|
||||||
|
get {
|
||||||
|
return Key(carbonKeyCode: carbonKeyCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
carbonKeyCode = newValue?.carbonKeyCode ?? 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var modifiers: NSEvent.ModifierFlags {
|
||||||
|
get {
|
||||||
|
return NSEvent.ModifierFlags(carbonFlags: carbonModifiers)
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
carbonModifiers = newValue.carbonFlags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var isValid: Bool {
|
||||||
|
return carbonKeyCode >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initializers
|
||||||
|
|
||||||
|
public init(carbonKeyCode: UInt32, carbonModifiers: UInt32 = 0) {
|
||||||
|
self.carbonKeyCode = carbonKeyCode
|
||||||
|
self.carbonModifiers = carbonModifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(key: Key, modifiers: NSEvent.ModifierFlags = []) {
|
||||||
|
self.carbonKeyCode = key.carbonKeyCode
|
||||||
|
self.carbonModifiers = modifiers.carbonFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Converting Keys
|
||||||
|
|
||||||
|
public static func carbonKeyCodeToString(_ carbonKeyCode: UInt32) -> String? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension KeyCombo {
|
||||||
|
public var dictionary: [String: Any] {
|
||||||
|
return [
|
||||||
|
"keyCode": Int(carbonKeyCode),
|
||||||
|
"modifiers": Int(carbonModifiers)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
public init?(dictionary: [String: Any]) {
|
||||||
|
guard let keyCode = dictionary["keyCode"] as? Int,
|
||||||
|
let modifiers = dictionary["modifiers"] as? Int
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(carbonKeyCode: UInt32(keyCode), carbonModifiers: UInt32(modifiers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension KeyCombo: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
var output = modifiers.description
|
||||||
|
|
||||||
|
if let keyDescription = key?.description {
|
||||||
|
output += keyDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|
20
phpmon/Vendor/HotKey/LICENSE
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2017–2019 Sam Soffes, http://soff.es
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
70
phpmon/Vendor/HotKey/ModifierFlagsExtension.swift
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import AppKit
|
||||||
|
import Carbon
|
||||||
|
|
||||||
|
extension NSEvent.ModifierFlags {
|
||||||
|
public var carbonFlags: UInt32 {
|
||||||
|
var carbonFlags: UInt32 = 0
|
||||||
|
|
||||||
|
if contains(.command) {
|
||||||
|
carbonFlags |= UInt32(cmdKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.option) {
|
||||||
|
carbonFlags |= UInt32(optionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.control) {
|
||||||
|
carbonFlags |= UInt32(controlKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.shift) {
|
||||||
|
carbonFlags |= UInt32(shiftKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
return carbonFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(carbonFlags: UInt32) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
if carbonFlags & UInt32(cmdKey) == UInt32(cmdKey) {
|
||||||
|
insert(.command)
|
||||||
|
}
|
||||||
|
|
||||||
|
if carbonFlags & UInt32(optionKey) == UInt32(optionKey) {
|
||||||
|
insert(.option)
|
||||||
|
}
|
||||||
|
|
||||||
|
if carbonFlags & UInt32(controlKey) == UInt32(controlKey) {
|
||||||
|
insert(.control)
|
||||||
|
}
|
||||||
|
|
||||||
|
if carbonFlags & UInt32(shiftKey) == UInt32(shiftKey) {
|
||||||
|
insert(.shift)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSEvent.ModifierFlags: CustomStringConvertible {
|
||||||
|
public var description: String {
|
||||||
|
var output = ""
|
||||||
|
|
||||||
|
if contains(.control) {
|
||||||
|
output += Key.control.description
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.option) {
|
||||||
|
output += Key.option.description
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.shift) {
|
||||||
|
output += Key.shift.description
|
||||||
|
}
|
||||||
|
|
||||||
|
if contains(.command) {
|
||||||
|
output += Key.command.description
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
}
|