1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-09 04:42:59 +02:00

Compare commits

...

82 Commits

Author SHA1 Message Date
a407515534 🚀 Version 5.6.1 2022-09-24 12:56:14 +02:00
b827ffb869 🔧 Bump version number 2022-09-23 16:46:23 +02:00
bdb718598e 🐛 Various bugfixes
- Fixes issue with `scanApps` being non-optional in custom configuration
- Fixes issue with position of separator if Xdebug is not detected
- Ensure that `isRunningSwiftUIPreview` modifier always return false for debug builds
2022-09-23 16:46:13 +02:00
ddfc73e033 🐛 Resolve issue with determining PATH (#194)
In previous builds, PHP Monitor would use interactive mode when opening
a /bin/zsh shell, in order to be able to load the full PATH.

This is a problem because when launching PHP Monitor via the command
line or when you have some interactivity in an actual interactive
shell, it is possible to get stuck waiting for a particular input
which will effectively 'freeze' PHP Monitor's shell.

There are two options to fix this:
1) work with a timeout, which may or may not return a PATH
2) use a non-interactive shell and source .zshrc

I chose option 2, which is the more robust choice. If no .zshrc file
exists, it is also not sourced to avoid warnings or errors from ending
up in the PATH.
2022-09-23 16:44:06 +02:00
cfae520984 👌 Correctly resolve tagged version (#195) 2022-09-22 18:43:09 +02:00
0c176493e5 🔀 Merge branch 'main' into dev/5.6 2022-09-18 14:08:48 +02:00
71da62f954 📝 Update contribution guidelines 2022-09-18 14:08:10 +02:00
d6781568a3 📝 Update README 2022-09-18 13:39:40 +02:00
c9c7e14416 👌 Add support for <= and < version constraints
I am entirely unsure why one would need these, but I figured I'd get
these in the app before I start the work on PHP Monitor 6.0.

This ensures all common version constraints can now be parsed correctly.
2022-09-18 00:05:37 +02:00
0947dc5ecc 🚀 Version 5.6 2022-09-16 19:33:42 +02:00
286cdd00e9 🔧 Prepare for release 2022-09-16 19:31:32 +02:00
42b79d3cb3 🔧 Upgrade to Xcode 14 2022-09-10 21:44:17 +02:00
36aa41568c 🐛 Fix issue with minimum width w/ hidden UI 2022-09-10 21:43:26 +02:00
273c51f702 🔧 Update credits URLs 2022-09-09 21:41:20 +02:00
186f80c90e Allow more UI tweaking 2022-09-08 18:14:22 +02:00
d93af814c9 👌 Fix extension visibility of NSMenu 2022-09-08 17:21:41 +02:00
f4885f7dbc 👌 Update pre-release PHP version notice 2022-09-08 17:20:30 +02:00
805c9f5e6a Add new Preferences panel for UI tweaking 2022-09-07 21:40:43 +02:00
183d0bbc30 Allow hiding of global version switcher 2022-09-07 21:14:56 +02:00
4855c14d28 ♻️ Refactor Preferences & PreferenceName
Also added a few new preferences related to toggling specific menu items
based on your personal choices.

(These new settings still need to be added to the UI.)
2022-09-07 20:33:23 +02:00
588398ea76 🍱 New screenshots with updated UI 2022-09-06 18:02:27 +02:00
d2502cfba2 Improved site isolation switch (#191) 2022-09-05 21:09:31 +02:00
8d46fa8a4e 👌 Right-clicking a row selects it in domain list 2022-09-05 20:05:16 +02:00
e59b89ea49 🔧 Tweak URLs to reference phpmon.app domain 2022-09-05 19:55:41 +02:00
18b103bf9c 🚀 Version 5.5.1
This release fixes an issue that could occur if your username and your
home directory have different names. (#189)
2022-09-01 17:30:55 +02:00
9b8de47f5d Fix file membership so all tests pass again 2022-09-01 17:27:17 +02:00
da2934c2e5 Fix file membership so all tests pass again 2022-08-31 17:08:51 +02:00
9de4fc6712 🐛 Don't use whoami and use NSHomeDirectory()
- This commit replaces the usage of `whoami` with `id -un`.
- This also changes all `~` replacements with the result of calling
  the `NSHomeDirectory()` which may differ from `id -un` (#189)
2022-08-29 18:15:13 +02:00
4f4c950349 🐛 Don't use whoami and use NSHomeDirectory()
- This commit replaces the usage of `whoami` with `id -un`.
- This also changes all `~` replacements with the result of calling
  the `NSHomeDirectory()` which may differ from `id -un` (#189)
2022-08-29 18:06:11 +02:00
331ca8a9a4 🚀 Version 5.5 2022-08-28 15:24:28 +02:00
87e08e4607 🔧 Use icon for stable build 2022-08-24 20:33:35 +02:00
845235a276 🔧 Bump build number for new build 2022-08-24 20:28:12 +02:00
7587126a42 🐛 Potential fix for parsing Valet version (#188) 2022-08-24 20:27:40 +02:00
f4b1e0745a 🐛 Potential fix for parsing Valet version (#188) 2022-08-24 20:27:19 +02:00
c7bb4c1d37 ♻️ Refactor submenu creation 2022-08-22 19:10:36 +02:00
a17512bfad 📝 Update README 2022-08-22 18:48:43 +02:00
a85e016b4a 📝 Update README 2022-08-22 18:47:47 +02:00
1292e91b33 ♻️ Cleanup 2022-08-18 17:38:46 +02:00
8c55fee18d ♻️ Refactor NSMenu structuring 2022-08-18 02:12:24 +02:00
663082d725 ♻️ Refactor NSMenu structuring
* Added a different way to create menus
* Added NoDomainResultsView (WIP)
2022-08-18 01:59:06 +02:00
fcdb4a8993 Allow opening of PHP Doctor via First Aid menu 2022-08-16 19:35:18 +02:00
9134f08ec9 👌 Fix no warnings view 2022-08-16 19:29:15 +02:00
b281df3bbd ♻️ Correctly refresh warnings 2022-08-15 01:57:22 +02:00
a9f9c38e0d ♻️ Rework how the user's PATH is loaded 2022-08-15 01:47:55 +02:00
bbbdce6b44 ♻️ Reworked helper scripts
- Add 'Welcome Tour' to First Aid menu
- Updated 'Welcome Tour'
- Helpers are now always written to ~/.config/phpmon/bin
- Updated helpers (now symlinked)
- Updated checks for when to symlink helpers
2022-08-14 23:57:46 +02:00
237185995d 🔥 Removed assets from tour 2022-08-14 22:23:39 +02:00
48f950fc3a 🐛 Fix translation string (#185)
This ensures that the "failed to check for update" modal now displays
the correct localized string.
2022-08-14 22:22:06 +02:00
2ee0934080 🔧 Make PHP Doctor optional (preference) 2022-08-13 23:16:10 +02:00
cfbb83976d 👌 Tweak preview 2022-08-12 21:07:38 +02:00
4e5b178e36 🏗 WIP: Tweaks to PHP Doctor 2022-08-12 20:50:27 +02:00
0c59d14da5 🐛 Fix isWriteableFile:atPath 2022-08-11 22:41:47 +02:00
2a9c8e6830 Check if /usr/local/bin helper is writable 2022-08-10 21:20:11 +02:00
78e750d764 Check if running the app with Rosetta 2022-08-10 21:04:23 +02:00
c1c7561361 🏗 WIP: Warning manager 2022-08-09 21:55:59 +02:00
f90925ee17 🏗 WIP: Warnings window & views 2022-08-09 20:31:17 +02:00
ccfb15c83c ♻️ Use WindowController instead of WC
- Renamed `ProgressWC` to `TerminalProgressWindowController` so it
  better reflects the functionality of the class.

- Renamed all `-WC` suffixed classes to `-WindowController`. This is
  cleaner because it avoids referencing water closets. 🚽

- Fixed some instances where the copyright notice used the wrong
  filename. All window controller classes are now accurate.
2022-08-09 18:04:18 +02:00
bcc80b5210 👌 More SwiftUI tuning (automatic height) 2022-08-05 20:05:29 +02:00
023043a81d 🍱 Further tweaks to onboarding view 2022-08-03 19:10:13 +02:00
a2c93833df 🍱 Tweaked onboarding view 2022-08-01 22:39:21 +02:00
8b6267f411 👌 Fix WarningView styling 2022-08-01 21:18:20 +02:00
1bff75311b Add WarningView 2022-07-31 21:27:52 +02:00
7a580eef0c 📝 Update README 2022-07-29 18:14:24 +02:00
2306529936 ♻️ Differentiate between folder and file existence
This also fixes #182, because it introduces a check for Valet's config
directory, which does not exist if Valet has not been installed yet.
2022-07-28 21:26:41 +02:00
08fdcdbc6c 👌 Simple cleanup 2022-07-27 20:38:39 +02:00
8cb8e5e409 🍱 Visual changes to onboarding screen 2022-07-26 20:30:19 +02:00
6094f83e98 👌 Do not require window from storyboard 2022-07-26 19:36:27 +02:00
cdb4b60487 👌 Add warning message for WIP launch screen 2022-07-26 19:34:30 +02:00
ae5bed2532 🔀 Merge branch 'feature/onboarding' into dev/5.5 2022-07-26 19:32:45 +02:00
f098ffbf3d ♻️ Consistency in naming of window controllers 2022-07-26 19:30:35 +02:00
0a55b45c60 🔧 Prepare 5.5 branch for dev builds 2022-07-25 21:40:43 +02:00
418d1e2479 Add support for custom environment vars (#183)
This commit adds support for custom environment variables, which can be
set in PHP Monitor's custom configuration file.

Let's say you wish to customize the `COMPOSER_HOME` env variable, like
in #183. You can fix this like so:

```json
{
    "scan_apps": [],
    "services": [],
    "presets": []
    "export": {
        "COMPOSER_HOME": "/absolute/path/to/composer/folder"
    }
}
```

Please note that while it is possible to set the `PATH` this way, you
WILL MOST CERTAINLY break PHP Monitor in the process. Setting other
environment variables should generally not pose an issue, unless said
environment variable affects the output of the shell that PHP Monitor
uses internally.
2022-07-25 21:40:02 +02:00
fa3ec2aaa3 🏗 WIP: Present OnboardingWC if launch count >= 1 2022-07-25 21:11:24 +02:00
a1df2deec5 👌 Cleanup 2022-07-25 20:48:26 +02:00
8fb43f7a16 🍱 Fix indentation 2022-07-13 22:22:30 +02:00
8a8d32cb5d 🍱 Tweaked onboarding screen 2022-07-13 11:27:32 +02:00
6e3bb1d322 Added onboarding sample 2022-07-08 21:13:46 +02:00
cac7048d92 👌 Use MainActor for methods 2022-07-08 20:44:40 +02:00
1aa051e12c 🔀 Merge branch 'dev/5.4' into dev/5.5 2022-07-08 20:42:28 +02:00
4ef5918b7a 🔀 Merge branch 'dev/5.4' into dev/5.5 2022-06-23 17:36:22 +02:00
cf03727dc0 🏗 Use @MainActor on MainMenu 2022-06-19 13:08:54 +02:00
25a7dded73 🐛 Fix typo when PHP switch failed 2022-06-19 13:08:19 +02:00
c5fd43312f 🏗 Use @MainActor for BetterAlert 2022-06-18 17:59:43 +02:00
75 changed files with 2252 additions and 788 deletions

23
.github/contributing.md vendored Normal file
View File

@@ -0,0 +1,23 @@
# Contribution Guidelines
Thank you for your interest in contributing to PHP Monitor.
I consider this project a bit of a nice side-project to my daily gig, so it is very much a personal affair where I love to tinker around.
**While the code of the latest PHP Monitor release is public, many things are constantly in flux that may not be pushed to this repository yet.**
I don't mean to be rude, but I don't want other people involved with the project beyond simply contributing a few small things here and there, as has been the case in the past.
The extra mental overhead of having additional contributors to report to, whose code will need to be reviewed... it's a lot and it makes working on PHP Monitor less enjoyable for me.
Plus, at this point, the majority of PHP Monitor's main functionality is also done.
As a result, I may refer you to this file at some point. Again, I don't wish to be rude, but this general rule stands:
**Making any changes in a fork and opening a pull request without opening an issue first will most likely result in your PR being closed without mercy.**
To repeat, I am **not opposed** to small contributions and fixes, if they are **meaningful or insightful**.
To learn more, please check out the [pull request template](/.github/pull_request_template.md) which contains more information about my contribution requirements. (This will also show up when you open a new PR.)
Thank you for respecting this!

View File

@@ -16,7 +16,7 @@ In short: It is usually best to *get in touch first* if you are making substanti
## About destination branches ## About destination branches
Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Merge requests that target `main` will be closed without mercy.** Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Pull requests that target `main` will be closed without mercy.**
Usually, the best target is the stable `dev/x.x` branch that corresponds with the latest major version that is released. Usually, the best target is the stable `dev/x.x` branch that corresponds with the latest major version that is released.

View File

@@ -33,6 +33,8 @@
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */; }; 54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */; };
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; }; 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; }; 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
C40508AF28ADA23D008FAC1F /* NoDomainResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40508AE28ADA23D008FAC1F /* NoDomainResultsView.swift */; };
C40508B128ADAB44008FAC1F /* NSMenuItemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */; };
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; };
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; };
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; }; C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; };
@@ -78,6 +80,7 @@
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; }; C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; }; C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; }; C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */; };
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; }; C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; }; C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; }; C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
@@ -85,16 +88,18 @@
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; }; C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; }; C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; }; C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDA928A2C49900CEAC97 /* WarningListView.swift */; };
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; }; C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; }; C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; }; C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; }; C4297F7A28970D59004C4630 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42800A928452AA10099C999 /* StatusMenu+Items.swift */; };
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; }; C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; }; C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; };
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; }; C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; };
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; }; C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; };
C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; };
C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */; };
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; }; C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; }; C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; }; C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; };
@@ -114,8 +119,10 @@
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; }; C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; }; C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; }; C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; };
C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; };
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; }; C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; }; C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
@@ -123,13 +130,15 @@
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; }; C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; }; C44F868E2835BD8D005C353A /* phpmon-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C44F868D2835BD8D005C353A /* phpmon-config.json */; };
C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C450C8C528C919EC002A2B4B /* PreferenceName.swift */; };
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; }; C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; };
C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C45E76142854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; }; C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45E76132854A65300B4FE0C /* ServicesManager.swift */; };
C463E380284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C463E380284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
C463E381284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C463E381284930EE00422731 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; };
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; }; C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; };
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; }; C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */; };
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; }; C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; }; C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; }; C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; };
@@ -144,6 +153,8 @@
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; }; C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; }; C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
@@ -151,6 +162,23 @@
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; }; C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; }; C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */; };
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
C485706F28BF452300539B36 /* WarningsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */; };
C485707028BF452300539B36 /* WarningsWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */; };
C485707128BF452E00539B36 /* WarningManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699EE28A2F2A30060FEB8 /* WarningManager.swift */; };
C485707228BF453800539B36 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; };
C485707328BF454300539B36 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */; };
C485707428BF454E00539B36 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B6091C2853AB9700C95265 /* ServicesView.swift */; };
C485707528BF454F00539B36 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4709CA128524B3400088BB8 /* StatsView.swift */; };
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B609192853AAD300C95265 /* SectionHeaderView.swift */; };
C485707728BF455300539B36 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; };
C485707828BF456300539B36 /* Warning.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47699F028A2F3150060FEB8 /* Warning.swift */; };
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; };
C485707A28BF457800539B36 /* WarningListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C422DDA928A2C49900CEAC97 /* WarningListView.swift */; };
C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; };
C485707C28BF459500539B36 /* NoWarningsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */; };
C485707D28BF45A200539B36 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; };
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; }; C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; }; C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
@@ -159,8 +187,12 @@
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; }; C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; }; C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; }; C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */; };
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PreferencesWindowController.swift */; };
C4A81CA428C67101008DD9D1 /* PMTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A81CA328C67101008DD9D1 /* PMTableView.swift */; };
C4A81CA528C67101008DD9D1 /* PMTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A81CA328C67101008DD9D1 /* PMTableView.swift */; };
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
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 */; };
@@ -196,6 +228,8 @@
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */; }; C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */; };
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 */; };
C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */; };
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3643828AE4FCE00C0770E /* StatusMenu+Items.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 */; };
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; }; C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
@@ -204,6 +238,8 @@
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; }; C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
C4CDA893288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
C4CDA894288F1A71007CE25F /* Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CDA892288F1A71007CE25F /* Keys.swift */; };
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
@@ -226,6 +262,7 @@
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; }; C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; }; C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; }; C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */; };
C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; }; C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E428551F9B006F9937 /* HeaderView.swift */; };
C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; }; C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EB53E628553117006F9937 /* ArrayExtension.swift */; };
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; }; C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
@@ -263,6 +300,9 @@
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; }; C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; };
C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */; };
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */; };
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; }; C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; }; C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F361602836BFD9003598CC /* MainMenu+Actions.swift */; };
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; }; C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
@@ -296,6 +336,8 @@
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>"; };
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; }; 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
C40508AE28ADA23D008FAC1F /* NoDomainResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoDomainResultsView.swift; sourceTree = "<group>"; };
C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuItemExtension.swift; sourceTree = "<group>"; };
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; }; C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; }; C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
@@ -326,18 +368,22 @@
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; }; C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; }; C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; }; C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoWarningsView.swift; sourceTree = "<group>"; };
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = "<group>"; }; C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = "<group>"; };
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; }; C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = "<group>"; }; C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = "<group>"; };
C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = "<group>"; }; C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = "<group>"; };
C422DDA928A2C49900CEAC97 /* WarningListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningListView.swift; sourceTree = "<group>"; };
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningsWindowController.swift; sourceTree = "<group>"; };
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; }; C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = "<group>"; }; C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = "<group>"; };
C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; }; C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; };
C42800A928452AA10099C999 /* StatusMenu+Items.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Items.swift"; sourceTree = "<group>"; }; C4297F7928970D59004C4630 /* WarningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningView.swift; sourceTree = "<group>"; };
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; }; C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; };
C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = "<group>"; }; C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = "<group>"; };
C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = "<group>"; }; C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = "<group>"; };
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = "<group>"; }; C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = "<group>"; };
C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Shell+PATH.swift"; sourceTree = "<group>"; };
C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = "<group>"; }; C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = "<group>"; };
C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = "<group>"; }; C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = "<group>"; };
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; }; C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; };
@@ -350,15 +396,17 @@
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; }; C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = "<group>"; };
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; }; C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = "<group>"; };
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; }; C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = "<group>"; };
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; }; C44A874728905BB000498BC4 /* ProgressVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressVC.swift; sourceTree = "<group>"; };
C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalProgressWindowController.swift; sourceTree = "<group>"; };
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; }; C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; }; C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; }; C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; }; C44F868D2835BD8D005C353A /* phpmon-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "phpmon-config.json"; sourceTree = "<group>"; };
C450C8C528C919EC002A2B4B /* PreferenceName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceName.swift; sourceTree = "<group>"; };
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; }; C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; };
C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; }; C45E76132854A65300B4FE0C /* ServicesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServicesManager.swift; sourceTree = "<group>"; };
C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; }; C463E37F284930EE00422731 /* PresetHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresetHelper.swift; sourceTree = "<group>"; };
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = "<group>"; }; C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWindowController.swift; sourceTree = "<group>"; };
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; }; C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; };
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; }; C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; }; C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
@@ -370,6 +418,8 @@
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; }; C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; }; C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; }; C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WarningManager.swift; sourceTree = "<group>"; };
C47699F028A2F3150060FEB8 /* Warning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Warning.swift; sourceTree = "<group>"; };
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; }; C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
@@ -379,7 +429,9 @@
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; }; C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; }; C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; }; C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; }; C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentCheck.swift; sourceTree = "<group>"; };
C4998F092617633900B2526E /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
C4A81CA328C67101008DD9D1 /* PMTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMTableView.swift; sourceTree = "<group>"; };
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; }; C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; }; C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; }; C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
@@ -401,11 +453,13 @@
C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyScanner.swift; sourceTree = "<group>"; }; C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyScanner.swift; sourceTree = "<group>"; };
C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ValetProxy+Fake.swift"; sourceTree = "<group>"; }; C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ValetProxy+Fake.swift"; sourceTree = "<group>"; };
C4C1019A27C65C6F001FACC2 /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = "<group>"; }; C4C1019A27C65C6F001FACC2 /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = "<group>"; };
C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Items.swift"; sourceTree = "<group>"; };
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = "<group>"; }; C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = "<group>"; };
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; }; C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; }; C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; }; C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; }; C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
C4CDA892288F1A71007CE25F /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = "<group>"; };
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; }; C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; }; C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = "<group>"; }; C4D5CFC927E0F9CD00035329 /* NginxConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationFile.swift; sourceTree = "<group>"; };
@@ -420,6 +474,7 @@
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; }; C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
C4EB53E428551F9B006F9937 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; }; C4EB53E428551F9B006F9937 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
C4EB53E628553117006F9937 /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.swift; sourceTree = "<group>"; }; C4EB53E628553117006F9937 /* ArrayExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayExtension.swift; sourceTree = "<group>"; };
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; }; C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
@@ -438,6 +493,8 @@
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = "<group>"; }; C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = "<group>"; };
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PreferencesWindowController+Hotkey.swift"; sourceTree = "<group>"; };
C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWindowController.swift; sourceTree = "<group>"; };
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; }; C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; };
C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; }; C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -463,14 +520,17 @@
5420395726135DB800FB00FA /* Preferences */ = { 5420395726135DB800FB00FA /* Preferences */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C4998F092617633900B2526E /* PrefsWC.swift */, C4998F092617633900B2526E /* PreferencesWindowController.swift */,
C4FACE7F288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift */,
5420395826135DC100FB00FA /* PrefsVC.swift */, 5420395826135DC100FB00FA /* PrefsVC.swift */,
C450C8C528C919EC002A2B4B /* PreferenceName.swift */,
5420395E2613607600FB00FA /* Preferences.swift */, 5420395E2613607600FB00FA /* Preferences.swift */,
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */, C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */, C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */,
C4DEB7D327A5D60B00834718 /* Stats.swift */, C4DEB7D327A5D60B00834718 /* Stats.swift */,
C41CD0272628D8E20065BBED /* Keybinds */, C41CD0272628D8E20065BBED /* Keybinds */,
54FCFD28276C88C0004CE748 /* Views */, 54FCFD28276C88C0004CE748 /* Views */,
C4CDA892288F1A71007CE25F /* Keys.swift */,
); );
path = Preferences; path = Preferences;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -580,6 +640,7 @@
C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853D2770FE3900DA4FBE /* Command.swift */,
C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */,
C4B5853C2770FE3900DA4FBE /* Shell.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */,
C42E3BF328A9BF5100AFECFC /* Shell+PATH.swift */,
C4C1019A27C65C6F001FACC2 /* Process.swift */, C4C1019A27C65C6F001FACC2 /* Process.swift */,
C40C7F2F27722E8D00DDDCDC /* Logger.swift */, C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
C417DC73277614690015E6EE /* Helpers.swift */, C417DC73277614690015E6EE /* Helpers.swift */,
@@ -645,6 +706,8 @@
C4D9ADBD27761084007277F4 /* PHP */, C4D9ADBD27761084007277F4 /* PHP */,
C47331A0247093AC009A0597 /* Menu */, C47331A0247093AC009A0597 /* Menu */,
C464ADAA275A7A25003FCD53 /* DomainList */, C464ADAA275A7A25003FCD53 /* DomainList */,
C422DDAB28A2DAA100CEAC97 /* Warnings */,
C44A874628905B8500498BC4 /* Onboarding */,
5420395726135DB800FB00FA /* Preferences */, 5420395726135DB800FB00FA /* Preferences */,
C44C198F276E3A380072762D /* Progress */, C44C198F276E3A380072762D /* Progress */,
C4C8E81D276F5686003AC782 /* Watcher */, C4C8E81D276F5686003AC782 /* Watcher */,
@@ -654,6 +717,16 @@
path = Domain; path = Domain;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
isa = PBXGroup;
children = (
C47699F028A2F3150060FEB8 /* Warning.swift */,
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
);
path = Warnings;
sourceTree = "<group>";
};
C42337A1281F19DC00459A48 /* Extensions */ = { C42337A1281F19DC00459A48 /* Extensions */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -662,6 +735,16 @@
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C4297F7828970D4E004C4630 /* Warning */ = {
isa = PBXGroup;
children = (
C4297F7928970D59004C4630 /* WarningView.swift */,
C422DDA928A2C49900CEAC97 /* WarningListView.swift */,
C41C708C28AA7F7900E8D498 /* NoWarningsView.swift */,
);
path = Warning;
sourceTree = "<group>";
};
C44067F327E256560045BD4E /* Cells */ = { C44067F327E256560045BD4E /* Cells */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -675,10 +758,19 @@
path = Cells; path = Cells;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C44A874628905B8500498BC4 /* Onboarding */ = {
isa = PBXGroup;
children = (
C4FACE82288F1F9700FC478F /* OnboardingWindowController.swift */,
);
path = Onboarding;
sourceTree = "<group>";
};
C44C198F276E3A380072762D /* Progress */ = { C44C198F276E3A380072762D /* Progress */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C44C198C276E3A1C0072762D /* ProgressWindow.swift */, C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */,
C44A874728905BB000498BC4 /* ProgressVC.swift */,
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */, C44C1990276E44CB0072762D /* ProgressWindow.storyboard */,
); );
path = Progress; path = Progress;
@@ -742,13 +834,14 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C44067F327E256560045BD4E /* Cells */, C44067F327E256560045BD4E /* Cells */,
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */, C464ADAB275A7A3F003FCD53 /* DomainListWindowController.swift */,
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */, C464ADAE275A7A69003FCD53 /* DomainListVC.swift */,
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */, C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */,
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */, C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */,
C4FE011028084FC200D1DE6D /* SelectionVC.swift */, C4FE011028084FC200D1DE6D /* SelectionVC.swift */,
C4930849279F331F009C240B /* AddSiteVC.swift */, C4930849279F331F009C240B /* AddSiteVC.swift */,
C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */, C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */,
C4A81CA328C67101008DD9D1 /* PMTableView.swift */,
); );
path = DomainList; path = DomainList;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -763,7 +856,7 @@
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */, C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */,
C4F361602836BFD9003598CC /* MainMenu+Actions.swift */, C4F361602836BFD9003598CC /* MainMenu+Actions.swift */,
C47331A1247093B7009A0597 /* StatusMenu.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */,
C42800A928452AA10099C999 /* StatusMenu+Items.swift */, C4C3643828AE4FCE00C0770E /* StatusMenu+Items.swift */,
); );
path = Menu; path = Menu;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -835,6 +928,7 @@
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */, C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
C4EED88827A48778006D7272 /* InterAppHandler.swift */, C4EED88827A48778006D7272 /* InterAppHandler.swift */,
C4D8016522B1584700C6DA1B /* Startup.swift */, C4D8016522B1584700C6DA1B /* Startup.swift */,
C495F5AE28A42E080087F70A /* EnvironmentCheck.swift */,
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */, C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
C40FE736282ABA4F00A302C2 /* AppVersion.swift */, C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
C45E76132854A65300B4FE0C /* ServicesManager.swift */, C45E76132854A65300B4FE0C /* ServicesManager.swift */,
@@ -878,6 +972,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */, C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */,
C40508AE28ADA23D008FAC1F /* NoDomainResultsView.swift */,
); );
path = Domains; path = Domains;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1005,9 +1100,19 @@
path = Switcher; path = Switcher;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C4E9D2BE2878B32D008FFDAD /* Onboarding */ = {
isa = PBXGroup;
children = (
C4E9D2BF2878B336008FFDAD /* OnboardingView.swift */,
);
path = Onboarding;
sourceTree = "<group>";
};
C4EE55B027708BB2001DF387 /* SwiftUI */ = { C4EE55B027708BB2001DF387 /* SwiftUI */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C4297F7828970D4E004C4630 /* Warning */,
C4E9D2BE2878B32D008FFDAD /* Onboarding */,
C4B609182853AAA700C95265 /* Domains */, C4B609182853AAA700C95265 /* Domains */,
C4B609172853AA9E00C95265 /* Menu */, C4B609172853AA9E00C95265 /* Menu */,
C4B609162853AA9A00C95265 /* Common */, C4B609162853AA9A00C95265 /* Common */,
@@ -1044,6 +1149,7 @@
C46FA23E246C358E00944F05 /* StringExtension.swift */, C46FA23E246C358E00944F05 /* StringExtension.swift */,
C48D0C9225CC804200CC7490 /* XibLoadable.swift */, C48D0C9225CC804200CC7490 /* XibLoadable.swift */,
C42759662627662800093CAE /* NSMenuExtension.swift */, C42759662627662800093CAE /* NSMenuExtension.swift */,
C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */,
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */, C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */,
C4EB53E628553117006F9937 /* ArrayExtension.swift */, C4EB53E628553117006F9937 /* ArrayExtension.swift */,
); );
@@ -1100,7 +1206,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 1320; LastSwiftUpdateCheck = 1320;
LastUpgradeCheck = 1320; LastUpgradeCheck = 1400;
ORGANIZATIONNAME = "Nico Verbruggen"; ORGANIZATIONNAME = "Nico Verbruggen";
TargetAttributes = { TargetAttributes = {
C41C1B3222B0097F00E7CF16 = { C41C1B3222B0097F00E7CF16 = {
@@ -1199,12 +1305,13 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C47699EF28A2F2A30060FEB8 /* WarningManager.swift in Sources */,
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */, C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */,
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */, C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */,
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
@@ -1214,11 +1321,13 @@
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */, 5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */,
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
C41C708D28AA7F7900E8D498 /* NoWarningsView.swift in Sources */,
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */, C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */, C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, 54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
C450C8C628C919EC002A2B4B /* PreferenceName.swift in Sources */,
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */, C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */, C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */, C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
@@ -1228,9 +1337,11 @@
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */, C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */, C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */, C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */,
C4E9D2C02878B336008FFDAD /* OnboardingView.swift in Sources */,
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */, C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */, C4EB53E528551F9B006F9937 /* HeaderView.swift in Sources */,
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */, C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
C44A874828905BB000498BC4 /* ProgressVC.swift in Sources */,
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */, C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
C4B585442770FE3900DA4FBE /* Command.swift in Sources */, C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */, C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */,
@@ -1238,11 +1349,13 @@
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */, C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */, C4B6091A2853AAD300C95265 /* SectionHeaderView.swift in Sources */,
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */,
C42800AA28452AA10099C999 /* StatusMenu+Items.swift in Sources */, C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */,
C415D3B72770F294005EF286 /* Actions.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */,
C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */,
C4CDA893288F1A71007CE25F /* Keys.swift in Sources */,
C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */, C4F361612836BFD9003598CC /* MainMenu+Actions.swift in Sources */,
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */, C44C198D276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */,
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */, 54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */, C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */,
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */, C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
@@ -1251,14 +1364,17 @@
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */, C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */,
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */,
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */, C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */, 54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
C4297F7A28970D59004C4630 /* WarningView.swift in Sources */,
C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */, C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */, C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */,
5420395F2613607600FB00FA /* Preferences.swift in Sources */, 5420395F2613607600FB00FA /* Preferences.swift in Sources */,
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */, C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */, 54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
C47699F128A2F3150060FEB8 /* Warning.swift in Sources */,
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */, 54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
@@ -1275,11 +1391,13 @@
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */, C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */, C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
C422DDAA28A2C49900CEAC97 /* WarningListView.swift in Sources */,
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */, C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */, C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */, C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */, C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */, C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */,
C4A81CA428C67101008DD9D1 /* PMTableView.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 */,
@@ -1288,14 +1406,17 @@
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */, C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */, C4B6091D2853AB9700C95265 /* ServicesView.swift in Sources */,
C40508B128ADAB44008FAC1F /* NSMenuItemExtension.swift in Sources */,
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */, C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */, C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */,
C476FF9822B0DD830098105B /* Alert.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */,
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
C485707028BF452300539B36 /* WarningsWindowController.swift in Sources */,
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */, C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */, C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */, 54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
@@ -1304,12 +1425,14 @@
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */, C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */,
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */, 54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */, C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */,
C40508AF28ADA23D008FAC1F /* NoDomainResultsView.swift in Sources */,
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */, C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */,
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
C42E3BF428A9BF5100AFECFC /* Shell+PATH.swift in Sources */,
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */, C42337A3281F19F000459A48 /* Xdebug.swift in Sources */,
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */, C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */, C464ADAC275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */,
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */, C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */,
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */, C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */, C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
@@ -1323,7 +1446,9 @@
files = ( files = (
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */, C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */,
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */, 54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
C485707128BF452E00539B36 /* WarningManager.swift in Sources */,
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */, C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
C4FACE81288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */,
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */, 54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */, C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */,
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */, C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */,
@@ -1334,20 +1459,27 @@
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */, C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */, C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */, C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
C44A874928905BB000498BC4 /* ProgressVC.swift in Sources */,
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */, C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */, C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */,
C485707528BF454F00539B36 /* StatsView.swift in Sources */,
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */, C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */, C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */, 54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
C485707328BF454300539B36 /* OnboardingView.swift in Sources */,
C485707728BF455300539B36 /* HeaderView.swift in Sources */,
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */, C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */, C4D5CFCB27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */, C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */, C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */, C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
C485707A28BF457800539B36 /* WarningListView.swift in Sources */,
C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */, C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */,
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */, C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */,
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */, C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */, C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */,
C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */,
C42E3BF528A9BF5100AFECFC /* Shell+PATH.swift in Sources */,
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
C4F319C927B034A500AFF46F /* Stats.swift in Sources */, C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
@@ -1364,24 +1496,30 @@
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */, C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */, C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */, C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
C485707428BF454E00539B36 /* ServicesView.swift in Sources */,
C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */, C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */,
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */, C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */, C4FC21B128391F8E00D368BB /* MainMenu+Actions.swift in Sources */,
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */, 54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */, C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
C450C8C728C919EC002A2B4B /* PreferenceName.swift in Sources */,
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */, C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */,
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */,
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */, C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */, C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */, C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */, C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
C485706D28BF450900539B36 /* NSMenuItemExtension.swift in Sources */,
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */, C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
C495F5B028A42E080087F70A /* EnvironmentCheck.swift in Sources */,
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */, C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */, C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
C40C5C9D2846A40600E28255 /* Preset.swift in Sources */, C40C5C9D2846A40600E28255 /* Preset.swift in Sources */,
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */, C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
C42800AB28452AA50099C999 /* StatusMenu+Items.swift in Sources */,
C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */, C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */,
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */, C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
@@ -1396,21 +1534,26 @@
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */, C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */, C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */, C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */,
C485707D28BF45A200539B36 /* WarningView.swift in Sources */,
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */, C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */, C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C4CDA894288F1A71007CE25F /* Keys.swift in Sources */,
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */, C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */, C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */,
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */, C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */, 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */, C4998F0B2617633900B2526E /* PreferencesWindowController.swift in Sources */,
C485707228BF453800539B36 /* SwiftUIHelper.swift in Sources */,
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */, C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */,
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */, C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */, C44C198E276E3A1C0072762D /* TerminalProgressWindowController.swift in Sources */,
C485707828BF456300539B36 /* Warning.swift in Sources */,
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */, C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */, C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */, C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */,
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */, C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */,
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */, C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
C485707C28BF459500539B36 /* NoWarningsView.swift in Sources */,
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */, C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
C40B24F227A310770018C7D2 /* Events.swift in Sources */, C40B24F227A310770018C7D2 /* Events.swift in Sources */,
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */, C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
@@ -1421,10 +1564,12 @@
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */, C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
C4F780B725D80B5D000DBC97 /* App.swift in Sources */, C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */, C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
C485707628BF455100539B36 /* SectionHeaderView.swift in Sources */,
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */, C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */, C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */, C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */,
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
C485706F28BF452300539B36 /* WarningsWindowController.swift in Sources */,
C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */, C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
@@ -1432,9 +1577,10 @@
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */, C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */, C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
C4A81CA528C67101008DD9D1 /* PMTableView.swift in Sources */,
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */, C45E76152854A65300B4FE0C /* ServicesManager.swift in Sources */,
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */, C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */,
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */, C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */, C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */,
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
@@ -1502,6 +1648,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@@ -1564,6 +1711,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer"; CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -1593,7 +1741,8 @@
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 = 912; CURRENT_PROJECT_VERSION = 973;
DEAD_CODE_STRIPPING = YES;
DEBUG = YES; DEBUG = YES;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -1603,7 +1752,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.4.1; MARKETING_VERSION = 5.6.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1620,7 +1769,8 @@
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 = 912; CURRENT_PROJECT_VERSION = 973;
DEAD_CODE_STRIPPING = YES;
DEBUG = NO; DEBUG = NO;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -1630,7 +1780,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 5.4.1; MARKETING_VERSION = 5.6.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1643,6 +1793,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
INFOPLIST_FILE = "phpmon-tests/Info.plist"; INFOPLIST_FILE = "phpmon-tests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
@@ -1662,6 +1813,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
INFOPLIST_FILE = "phpmon-tests/Info.plist"; INFOPLIST_FILE = "phpmon-tests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1320" LastUpgradeVersion = "1400"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -68,6 +68,11 @@
</CommandLineArgument> </CommandLineArgument>
</CommandLineArguments> </CommandLineArguments>
<EnvironmentVariables> <EnvironmentVariables>
<EnvironmentVariable
key = "EXTREME_DOCTOR_MODE"
value = ""
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable <EnvironmentVariable
key = "PAINT_PHPMON_SWIFTUI_VIEWS" key = "PAINT_PHPMON_SWIFTUI_VIEWS"
value = "" value = ""

View File

@@ -1,4 +1,5 @@
> 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!** ⭐️ > **Note**
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider [sponsoring](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p> <p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
@@ -342,6 +343,7 @@ Here's an example of a working preset:
<pre> <pre>
{ {
"scan_apps": [], "scan_apps": [],
"services": [],
"presets": [ "presets": [
{ {
"name": "Legacy Project", "name": "Legacy Project",
@@ -355,11 +357,67 @@ Here's an example of a working preset:
"post_max_size": "128M" "post_max_size": "128M"
} }
} }
] ],
"export": {}
} }
</pre> </pre>
You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version. You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version.
> **Warning**
> You must restart PHP Monitor for these changes to be detected.
</details>
<details>
<summary><strong>How do I ensure additional Homebrew services are shown in the app?</strong></summary>
You must set these services up in a JSON file, located in `~/.config/phpmon/config.json`.
You can specify custom services in the configuration file for Homebrew services that run as your own user (not root).
> **Info**
> If your service must run as root, it cannot currently be added to PHP Monitor.
You can find out which services are available by running `brew services list`.
Here's an example where we add the `mailhog` and `mysql` services to PHP Monitor:
<pre>
{
"scan_apps": [],
"services": ["mailhog", "mysql"],
"presets": [],
"export": {}
}
</pre>
> **Warning**
> You must restart PHP Monitor for these changes to be detected.
</details>
<details>
<summary><strong>How do I set custom environment variables?</strong></summary>
You must configure these custom environment variables up in a JSON file, located in `~/.config/phpmon/config.json`.
PHP Monitor uses a default Shell environment, with no custom environment variables. You need to set custom environment variables manually. These are then used for e.g. Composer.
Here's an example of a working `COMPOSER_HOME` environment variable which is respected:
<pre>
{
"scan_apps": [],
"services": [],
"presets": [],
"export": {
"COMPOSER_HOME": "/absolute/path/to/composer/folder"
}
}
</pre>
> **Warning**
> You must restart PHP Monitor for these changes to be detected.
</details> </details>
<details> <details>
@@ -377,12 +435,14 @@ You can add your own apps by creating and editing a `~/.config/phpmon/config.jso
<pre> <pre>
{ {
"scan_apps": ["Xcode", "Kraken"], "scan_apps": ["Xcode", "Kraken"]
"presets": []
} }
</pre> </pre>
You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary. You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
> **Warning**
> You must restart PHP Monitor for these changes to be detected.
</details> </details>
<details> <details>
@@ -472,14 +532,14 @@ Donations really help with the Apple Developer Program cost, and keep me motivat
## 😎 Acknowledgements ## 😎 Acknowledgements
While I did make this application during my own free time, PHP Monitor started out from various learning experiments during work hours at my employer, DIVE. I'd also like to shout out the following folks: Special thanks go out to:
* My colleagues at [DIVE](https://dive.be) * Everyone supporting me via [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen)
* Everyone who has donated via [my sponsor page](https://nicoverbruggen.be/sponsor)
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors) * The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so * Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
* My [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen) and those who have donated * Everyone who has left feedback and reported bugs
* Everyone who has left feedback and reported bugs (appreciate it!) * Everyone in the Laravel community who shared the app, especially on Twitter
* Everyone in the Laravel community who shared the app (thanks!)
Thank you very much for your contributions, kind words and support. Thank you very much for your contributions, kind words and support.
@@ -513,7 +573,8 @@ If an extension or other process writes to a single file a bunch of times in a s
1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name. 1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name.
1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all. 1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all.
*Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly. > **Note**
> If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly.
### Want to know more? ### Want to know more?

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 KiB

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

After

Width:  |  Height:  |  Size: 519 KiB

View File

@@ -18,4 +18,30 @@ class AppUpdaterCheckTest: XCTestCase {
XCTAssertNotNil(version) XCTAssertNotNil(version)
} }
func testTaggedReleaseOmitsZeroPatch() {
let version = AppVersion.from("3.5.0_333")!
XCTAssertEqual(version.tagged, "3.5")
XCTAssertEqual(version.version, "3.5.0")
}
func testTaggedReleaseDoesntOmitNonZeroPatch() {
let version = AppVersion.from("3.5.1_333")!
XCTAssertEqual(version.tagged, "3.5.1")
XCTAssertEqual(version.version, "3.5.1")
}
func testTagTruncationDoesntAffectMajorVersions() {
var version = AppVersion.from("5.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0")
version = AppVersion.from("5.0.0_333")!
XCTAssertEqual(version.tagged, "5.0")
XCTAssertEqual(version.version, "5.0.0")
}
} }

View File

@@ -8,6 +8,7 @@
import XCTest import XCTest
// swiftlint:disable type_body_length
class PhpVersionNumberTest: XCTestCase { class PhpVersionNumberTest: XCTestCase {
func testCanDeconstructPhpVersion() throws { func testCanDeconstructPhpVersion() throws {
@@ -287,4 +288,76 @@ class PhpVersionNumberTest: XCTestCase {
.make(from: ["7.3.1", "7.2.9"]).all .make(from: ["7.3.1", "7.2.9"]).all
) )
} }
func testCanCheckLessThanOrEqualConstraints() throws {
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<=7.2", strict: true),
PhpVersionNumberCollection
.make(from: ["7.2", "7.1", "7.0"]).all
)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<=7.2.0", strict: true),
PhpVersionNumberCollection
.make(from: ["7.2", "7.1", "7.0"]).all
)
// Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<=7.2.5", strict: true),
PhpVersionNumberCollection
.make(from: ["7.2", "7.1", "7.0"]).all
)
// Non-strict check (ignoring patch has no effect)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<=7.2.5", strict: false),
PhpVersionNumberCollection
.make(from: ["7.2", "7.1", "7.0"]).all
)
}
func testCanCheckLessThanConstraints() throws {
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<7.2", strict: true),
PhpVersionNumberCollection
.make(from: ["7.1", "7.0"]).all
)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<7.2.0", strict: true),
PhpVersionNumberCollection
.make(from: ["7.1", "7.0"]).all
)
// Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<7.2.5", strict: true),
PhpVersionNumberCollection
.make(from: ["7.2", "7.1", "7.0"]).all
)
// Non-strict check (patch resolves to 7.2.999, which is bigger than 7.2.5)
XCTAssertEqual(
PhpVersionNumberCollection
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
.matching(constraint: "<7.2.5", strict: false),
PhpVersionNumberCollection
.make(from: ["7.1", "7.0"]).all
)
}
} }

View File

@@ -53,18 +53,22 @@ struct Constants {
struct Urls { struct Urls {
static let DonationPayment = URL( // phpmon.app URLs (these are aliased to redirect correctly)
string: "https://nicoverbruggen.be/sponsor#pay-now"
)!
static let DonationPage = URL( static let DonationPage = URL(
string: "https://nicoverbruggen.be/sponsor" string: "https://phpmon.app/sponsor"
)! )!
static let FrequentlyAskedQuestions = URL( static let FrequentlyAskedQuestions = URL(
string: "https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting" string: "https://phpmon.app/faq"
)! )!
static let DonationPayment = URL(
string: "https://phpmon.app/sponsor/now"
)!
// GitHub URLs (do not alias these)
static let GitHubReleases = URL( static let GitHubReleases = URL(
string: "https://github.com/nicoverbruggen/phpmon/releases" string: "https://github.com/nicoverbruggen/phpmon/releases"
)! )!

View File

@@ -21,7 +21,7 @@ public class Paths {
init() { init() {
baseDir = App.architecture != "x86_64" ? .opt : .usr baseDir = App.architecture != "x86_64" ? .opt : .usr
userName = String(Shell.pipe("whoami").split(separator: "\n")[0]) userName = String(Shell.pipe("id -un").split(separator: "\n")[0])
} }
public func detectBinaryPaths() { public func detectBinaryPaths() {
@@ -57,6 +57,10 @@ public class Paths {
return shared.userName return shared.userName
} }
public static var homePath: String {
return NSHomeDirectory()
}
public static var cellarPath: String { public static var cellarPath: String {
return "\(shared.baseDir.rawValue)/Cellar" return "\(shared.baseDir.rawValue)/Cellar"
} }

View File

@@ -0,0 +1,33 @@
//
// Shell+PATH.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 15/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
extension Shell {
var PATH: String {
let task = Process()
task.launchPath = "/bin/zsh"
let command = Filesystem.fileExists("~/.zshrc")
// source the user's .zshrc file if it exists to complete $PATH
? ". ~/.zshrc && echo $PATH"
// otherwise, non-interactive mode is sufficient
: "echo $PATH"
task.arguments = ["--login", "-lc", command]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
return String(data: data, encoding: String.Encoding.utf8) ?? ""
}
}

View File

@@ -32,6 +32,9 @@ public class Shell {
*/ */
public var shell: String = "/bin/sh" public var shell: String = "/bin/sh"
/** Additional exports that are sent if `requiresPath` is set to true. */
public var exports: String = ""
/** /**
Singleton to access a user shell (with --login) Singleton to access a user shell (with --login)
*/ */
@@ -114,13 +117,23 @@ public class Shell {
Creates a new process with the correct PATH and shell. Creates a new process with the correct PATH and shell.
*/ */
public func createTask(for command: String, requiresPath: Bool) -> Process { public func createTask(for command: String, requiresPath: Bool) -> Process {
let tailoredCommand = requiresPath var completeCommand = ""
? "export PATH=\(Paths.binPath):$PATH && \(command)"
: command if requiresPath {
// Basic export (PATH)
completeCommand += "export PATH=\(Paths.binPath):$PATH && "
// Put additional exports in between
if !self.exports.isEmpty {
completeCommand += "\(self.exports) && "
}
}
completeCommand += command
let task = Process() let task = Process()
task.launchPath = self.shell task.launchPath = self.shell
task.arguments = ["--noprofile", "-norc", "--login", "-c", tailoredCommand] task.arguments = ["--noprofile", "-norc", "--login", "-c", completeCommand]
return task return task
} }

View File

@@ -9,43 +9,17 @@
import Cocoa import Cocoa
extension NSMenu { extension NSMenu {
convenience init(items: [NSMenuItem], target: NSObject? = nil) {
open func addItem(_ newItem: NSMenuItem, withKeyModifier modifier: NSEvent.ModifierFlags) { self.init()
newItem.keyEquivalentModifierMask = modifier self.addItems(items, target: target)
self.addItem(newItem)
} }
} public func addItems(_ items: [NSMenuItem], target: NSObject? = nil) {
for item in items {
@IBDesignable class LocalizedMenuItem: NSMenuItem { self.addItem(item)
if target != nil {
@IBInspectable item.target = target
var localizationKey: String? { }
didSet {
self.title = localizationKey?.localized ?? self.title
} }
} }
}
// MARK: - NSMenuItem subclasses
class PhpMenuItem: NSMenuItem {
var version: String = ""
}
class XdebugMenuItem: NSMenuItem {
var mode: String = ""
}
class ExtensionMenuItem: NSMenuItem {
var phpExtension: PhpExtension?
}
class EditorMenuItem: NSMenuItem {
var editor: Application?
}
class PresetMenuItem: NSMenuItem {
var preset: Preset?
} }

View File

@@ -0,0 +1,87 @@
//
// NSMenuItem.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 18/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Cocoa
extension NSMenuItem {
convenience init(
title: String,
action: Selector? = nil,
keyEquivalent: String = "",
keyModifier: NSEvent.ModifierFlags = [],
toolTip: String? = nil
) {
self.init(title: title, action: action, keyEquivalent: keyEquivalent)
self.keyEquivalentModifierMask = keyModifier
self.toolTip = toolTip
}
convenience init(
title: String,
keyEquivalent: String = "",
keyModifier: NSEvent.ModifierFlags = [],
toolTip: String? = nil,
submenu: [NSMenuItem],
target: NSObject? = nil
) {
self.init(title: title, action: nil, keyEquivalent: keyEquivalent)
self.keyEquivalentModifierMask = keyModifier
self.toolTip = toolTip
self.submenu = NSMenu(items: submenu, target: target)
}
}
// MARK: - NSMenuItem subclasses
@IBDesignable class LocalizedMenuItem: NSMenuItem {
@IBInspectable var localizationKey: String? {
didSet {
self.title = localizationKey?.localized ?? self.title
}
}
}
class PhpMenuItem: NSMenuItem {
var version: String = ""
}
class XdebugMenuItem: NSMenuItem {
var mode: String = ""
}
class ExtensionMenuItem: NSMenuItem {
var phpExtension: PhpExtension?
}
class EditorMenuItem: NSMenuItem {
var editor: Application?
}
class PresetMenuItem: NSMenuItem {
var preset: Preset?
static func getAll() -> [NSMenuItem] {
return Preferences.custom.presets!.map { preset in
let presetMenuItem = PresetMenuItem(
title: preset.getMenuItemText(),
action: #selector(MainMenu.togglePreset(sender:))
)
if let attributedString = try? NSMutableAttributedString(
data: preset.getMenuItemText().data(using: .utf8)!,
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil
) {
presetMenuItem.attributedTitle = attributedString
}
presetMenuItem.preset = preset
return presetMenuItem
}
}
}

View File

@@ -5,6 +5,7 @@
// Copyright © 2022 Nico Verbruggen. All rights reserved. // Copyright © 2022 Nico Verbruggen. All rights reserved.
// //
import Foundation import Foundation
import SwiftUI
extension String { extension String {
var localized: String { var localized: String {
@@ -17,6 +18,10 @@ extension String {
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
} }
var localizedForSwiftUI: LocalizedStringKey {
return LocalizedStringKey(self.localized)
}
func localized(_ args: CVarArg...) -> String { func localized(_ args: CVarArg...) -> String {
String(format: self.localized, arguments: args) String(format: self.localized, arguments: args)
} }

View File

@@ -1,5 +1,5 @@
// //
// FileSystem.swift // Filesystem.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 07/12/2021. // Created by Nico Verbruggen on 07/12/2021.
@@ -7,17 +7,55 @@
// //
import Cocoa import Cocoa
import Foundation
class Filesystem { class Filesystem {
/** /**
Checks if a file exists at the provided path. Checks if a file or directory exists at the provided path.
Uses `FileManager`.
*/ */
public static func fileExists(_ path: String) -> Bool { public static func exists(_ path: String) -> Bool {
return FileManager.default.fileExists( return FileManager.default.fileExists(
atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)") atPath: path.replacingOccurrences(of: "~", with: Paths.homePath)
) )
} }
/**
Checks if a file exists at the provided path.
*/
public static func fileExists(_ path: String) -> Bool {
var isDirectory: ObjCBool = true
let exists = FileManager.default.fileExists(
atPath: path.replacingOccurrences(of: "~", with: Paths.homePath),
isDirectory: &isDirectory
)
return exists && !isDirectory.boolValue
}
/**
Checks if a directory exists at the provided path.
*/
public static func directoryExists(_ path: String) -> Bool {
var isDirectory: ObjCBool = true
let exists = FileManager.default.fileExists(
atPath: path.replacingOccurrences(of: "~", with: Paths.homePath),
isDirectory: &isDirectory
)
return exists && isDirectory.boolValue
}
/**
Checks if a given file is a symbolic link.
*/
public static func fileIsSymlink(_ path: String) -> Bool {
do {
let attribs = try FileManager.default.attributesOfItem(atPath: path)
return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink
} catch {
return false
}
}
} }

View File

@@ -10,7 +10,7 @@ import UserNotifications
class LocalNotification { class LocalNotification {
public static func send(title: String, subtitle: String, preference: PreferenceName) { @MainActor public static func send(title: String, subtitle: String, preference: PreferenceName) {
if !Preferences.isEnabled(preference) { if !Preferences.isEnabled(preference) {
return return
} }

View File

@@ -7,6 +7,7 @@
// //
import Foundation import Foundation
import Cocoa
class Xdebug { class Xdebug {
@@ -26,6 +27,26 @@ class Xdebug {
return value.components(separatedBy: ",").filter { self.modes.contains($0) } return value.components(separatedBy: ",").filter { self.modes.contains($0) }
} }
public static func asMenuItems() -> [NSMenuItem] {
var items: [NSMenuItem] = []
let activeModes = Self.activeModes
for mode in Self.modes {
let item = XdebugMenuItem(
title: mode,
action: #selector(MainMenu.toggleXdebugMode(sender:)),
keyEquivalent: ""
)
item.state = activeModes.contains(mode) ? .on : .off
item.mode = mode
items.append(item)
}
return items
}
public static var modes: [String] { public static var modes: [String] {
return [ return [
"develop", "develop",

View File

@@ -16,8 +16,18 @@ class PhpHelper {
// Take the PHP version (e.g. "7.2") and generate a dotless version // Take the PHP version (e.g. "7.2") and generate a dotless version
let dotless = version.replacingOccurrences(of: ".", with: "") let dotless = version.replacingOccurrences(of: ".", with: "")
// Determine the dotless name for this PHP version
let destination = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
// Check if the ~/.config/phpmon/bin directory is in the PATH
let inPath = Shell.user.PATH.contains("\(Paths.homePath)/.config/phpmon/bin")
// Check if we can create symlinks (`/usr/local/bin` must be writable)
let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
do { do {
let destination = "/usr/local/bin/pm\(dotless)" Shell.run("mkdir -p ~/.config/phpmon/bin")
if FileManager.default.fileExists(atPath: destination) { if FileManager.default.fileExists(atPath: destination) {
let contents = try String(contentsOfFile: destination) let contents = try String(contentsOfFile: destination)
if !contents.contains(keyPhrase) { if !contents.contains(keyPhrase) {
@@ -52,10 +62,40 @@ class PhpHelper {
// Make sure the file is executable // Make sure the file is executable
Shell.run("chmod +x \(destination)") Shell.run("chmod +x \(destination)")
// Create a symlink if the folder is not in the PATH
if !inPath {
// First, check if we can create symlinks at all
if !canWriteSymlinks {
Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.")
return
}
// Write the symlink
self.createSymlink(dotless)
}
} catch { } catch {
print(error) print(error)
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)") Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
} }
} }
private static func createSymlink(_ dotless: String) {
let source = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
let destination = "/usr/local/bin/pm\(dotless)"
if !Filesystem.fileExists(destination) {
Log.info("Creating new symlink: \(destination)")
Shell.run("ln -s \(source) \(destination)")
return
}
if !Filesystem.fileIsSymlink(destination) {
Log.info("Overwriting existing file with new symlink: \(destination)")
Shell.run("ln -fs \(source) \(destination)")
return
}
Log.info("Symlink in \(destination) already exists, OK.")
}
} }

View File

@@ -87,6 +87,14 @@ public struct PhpVersionNumberCollection: Equatable {
return self.versions.filter { $0.isNewerThan(version, strict) } return self.versions.filter { $0.isNewerThan(version, strict) }
} }
if let version = PhpVersionNumber.make(from: constraint, type: .smallerThanOrEqual) {
return self.versions.filter { $0.isSameAs(version, strict) || $0.isOlderThan(version, strict)}
}
if let version = PhpVersionNumber.make(from: constraint, type: .smallerThan) {
return self.versions.filter { $0.isOlderThan(version, strict)}
}
return [] return []
} }
} }
@@ -116,12 +124,8 @@ public struct PhpVersionNumber: Equatable, Hashable {
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
// TODO: (6.0) Handle these cases (even though I suspect these are uncommon)
/*
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"# case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
*/
} }
public static func parse(_ text: String) throws -> Self { public static func parse(_ text: String) throws -> Self {
@@ -175,6 +179,15 @@ public struct PhpVersionNumber: Equatable, Hashable {
) )
} }
internal func isOlderThan(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
return (
self.major < version.major ||
self.major == version.major && self.minor < version.minor ||
self.major == version.major && self.minor == version.minor
&& self.patch(strict) < version.patch(strict)
)
}
internal func hasNewerMinorVersionOrPatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool { internal func hasNewerMinorVersionOrPatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
return self.major == version.major && return self.major == version.major &&
( (

View File

@@ -32,18 +32,11 @@ class PhpConfigurationFile: CreatedFromFile {
/** Resolves a PHP configuration file (.ini) */ /** Resolves a PHP configuration file (.ini) */
static func from(filePath: String) -> Self? { static func from(filePath: String) -> Self? {
let path = filePath.replacingOccurrences( let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
of: "~",
with: "/Users/\(Paths.whoami)"
)
do { do {
let fileContents = try String(contentsOfFile: path) let fileContents = try String(contentsOfFile: path)
return Self.init(path: path, contents: fileContents)
return Self.init(
path: path,
contents: fileContents
)
} catch { } catch {
Log.warn("Could not read the PHP configuration file at: `\(filePath)`") Log.warn("Could not read the PHP configuration file at: `\(filePath)`")
return nil return nil
@@ -70,10 +63,8 @@ class PhpConfigurationFile: CreatedFromFile {
} }
public func getConfig(for key: String) -> ConfigValue? { public func getConfig(for key: String) -> ConfigValue? {
for (_, section) in self.content { for (_, section) in self.content where section.keys.contains(key) {
if section.keys.contains(key) { return section[key]!
return section[key]!
}
} }
return nil return nil
} }

View File

@@ -13,9 +13,9 @@
</head> </head>
<body> <body>
<br> <br>
<p><b>Want to spread the love?</b> Leave a <a href="https://github.com/nicoverbruggen/phpmon">star on GitHub</a>!</p> <p><b>Do you enjoy using the app?</b> Leave a <a href="https://phpmon.app/github">star on GitHub</a>!</p>
<p><b>Having issues?</b> Consult the <a href="https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting">FAQ & Troubleshooting</a> section.</p> <p><b>Having issues?</b> Consult the <a href="https://phpmon.app/faq">FAQ</a> section, I did my best to ensure everything is documented.</p>
<p><b>Want to support me?</b> You can <a href="https://nicoverbruggen.be/sponsor">financially support</a> the continued development of this app.</p> <p><b>Want to support further development of PHP Monitor?</b> You can <a href="https://phpmon.app/sponsor">financially support</a> the continued development of this app.</p>
<p><b>Get the latest on Twitter</b> Give me a <a href="https://twitter.com/nicoverbruggen">follow on Twitter</a> to learn about the latest and greatest updates of this app.</p> <p><b>Get the latest on Twitter</b> Give me a <a href="https://twitter.com/nicoverbruggen">follow on Twitter</a> to learn about the latest and greatest updates of this app.</p>
<br> <br>
</body> </body>

View File

@@ -51,10 +51,16 @@ class App {
var preferences: [PreferenceName: Bool]! var preferences: [PreferenceName: Bool]!
/** The window controller of the currently active preferences window. */ /** The window controller of the currently active preferences window. */
var preferencesWindowController: PrefsWC? var preferencesWindowController: PreferencesWindowController?
/** The window controller of the currently active site list window. */ /** The window controller of the currently active site list window. */
var domainListWindowController: DomainListWC? var domainListWindowController: DomainListWindowController?
/** The window controller of the onboarding window. */
var onboardingWindowController: OnboardingWindowController?
/** The window controller of the warnings window. */
var warningsWindowController: WarningsWindowController?
/** List of detected (installed) applications that PHP Monitor can work with. */ /** List of detected (installed) applications that PHP Monitor can work with. */
var detectedApplications: [Application] = [] var detectedApplications: [Application] = []
@@ -62,6 +68,9 @@ class App {
/** The services manager, responsible for figuring out what services are active/inactive. */ /** The services manager, responsible for figuring out what services are active/inactive. */
var services = ServicesManager.shared var services = ServicesManager.shared
/** The warning manager, responsible for keeping track of warnings. */
var warnings = WarningManager.shared
/** Timer that will periodically reload info about the user's PHP installation. */ /** Timer that will periodically reload info about the user's PHP installation. */
var timer: Timer? var timer: Timer?

View File

@@ -146,8 +146,9 @@ class AppUpdateChecker {
text: "updater.alerts.buttons.release_notes".localized, text: "updater.alerts.buttons.release_notes".localized,
action: { vc in action: { vc in
vc.close(with: .OK) vc.close(with: .OK)
NSWorkspace.shared.open( NSWorkspace.shared.open(
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.version)\(devSuffix)") Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.tagged)\(devSuffix)")
) )
} }
) )
@@ -161,14 +162,14 @@ class AppUpdateChecker {
private static func notifyAboutConnectionIssue() { private static func notifyAboutConnectionIssue() {
DispatchQueue.main.async { DispatchQueue.main.async {
BetterAlert().withInformation( BetterAlert().withInformation(
title: "updater.errors.cannot_check_for_update.title".localized, title: "updater.alerts.cannot_check_for_update.title".localized,
subtitle: "updater.errors.cannot_check_for_update.subtitle".localized, subtitle: "updater.alerts.cannot_check_for_update.subtitle".localized,
description: "updater.errors.cannot_check_for_update.description".localized( description: "updater.alerts.cannot_check_for_update.description".localized(
App.version App.version
) )
) )
.withTertiary( .withTertiary(
text: "updater.errors.buttons.releases_on_github".localized, text: "updater.alerts.buttons.releases_on_github".localized,
action: { _ in action: { _ in
NSWorkspace.shared.open(Constants.Urls.GitHubReleases) NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
} }

View File

@@ -66,6 +66,14 @@ class AppVersion {
return AppVersion.from("\(App.shortVersion)_\(App.bundleVersion)")! return AppVersion.from("\(App.shortVersion)_\(App.bundleVersion)")!
} }
var tagged: String {
if version.suffix(2) == ".0" && version.count > 3 {
return String(version.dropLast(2))
}
return version
}
var computerReadable: String { var computerReadable: String {
return "\(version)_\(build ?? "0")" return "\(version)_\(build ?? "0")"
} }

View File

@@ -325,7 +325,7 @@
<scene sceneID="PQa-AT-b2a"> <scene sceneID="PQa-AT-b2a">
<objects> <objects>
<customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/> <customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" customClass="PrefsWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController"> <windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" customClass="PreferencesWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="h4c-3b-nko"> <window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="h4c-3b-nko">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@@ -399,7 +399,7 @@
<!--Window Controller--> <!--Window Controller-->
<scene sceneID="4XS-kY-YIS"> <scene sceneID="4XS-kY-YIS">
<objects> <objects>
<windowController storyboardIdentifier="domainListWindow" id="8Ec-9q-82s" customClass="DomainListWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController"> <windowController storyboardIdentifier="domainListWindow" id="8Ec-9q-82s" customClass="DomainListWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" separatorStyle="line" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1"> <window key="window" separatorStyle="line" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@@ -821,7 +821,7 @@ Gw
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/> <rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj"> <tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj" customClass="PMTableView" customModule="PHP_Monitor" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="626" height="281"/> <rect key="frame" x="0.0" y="0.0" width="626" height="281"/>
<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"/>

View File

@@ -0,0 +1,45 @@
//
// EnvironmentCheck.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
/**
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
Checks that require an app restart will always lead to an alert and app termination shortly after.
*/
struct EnvironmentCheck {
let command: () async -> Bool
let name: String
let titleText: String
let subtitleText: String
let descriptionText: String
let buttonText: String
let requiresAppRestart: Bool
init(
command: @escaping () async -> Bool,
name: String,
titleText: String,
subtitleText: String,
descriptionText: String = "",
buttonText: String = "OK",
requiresAppRestart: Bool = false
) {
self.command = command
self.name = name
self.titleText = titleText
self.subtitleText = subtitleText
self.descriptionText = descriptionText
self.buttonText = buttonText
self.requiresAppRestart = requiresAppRestart
}
public func succeeds() async -> Bool {
return await !self.command()
}
}

View File

@@ -56,12 +56,14 @@ class InterApp {
if PhpEnv.shared.availablePhpVersions.contains(version) { if PhpEnv.shared.availablePhpVersions.contains(version) {
MainMenu.shared.switchToPhpVersion(version) MainMenu.shared.switchToPhpVersion(version)
} else { } else {
BetterAlert().withInformation( DispatchQueue.main.async {
title: "alert.php_switch_unavailable.title".localized, BetterAlert().withInformation(
subtitle: "alert.php_switch_unavailable.subtitle".localized(version) title: "alert.php_switch_unavailable.title".localized,
).withPrimary( subtitle: "alert.php_switch_unavailable.subtitle".localized(version)
text: "alert.php_switch_unavailable.ok".localized ).withPrimary(
).show() text: "alert.php_switch_unavailable.ok".localized
).show()
}
} }
}) })
]} ]}

View File

@@ -167,6 +167,18 @@ class Startup {
descriptionText: "startup.errors.services_json_error.desc".localized descriptionText: "startup.errors.services_json_error.desc".localized
), ),
// ================================================================================= // =================================================================================
// Determine that Valet is installed
// =================================================================================
EnvironmentCheck(
command: {
return !Filesystem.directoryExists("~/.config/valet")
},
name: "`.config/valet` not empty (Valet installed)",
titleText: "startup.errors.valet_not_installed.title".localized,
subtitleText: "startup.errors.valet_not_installed.subtitle".localized,
descriptionText: "startup.errors.valet_not_installed.desc".localized
),
// =================================================================================
// Determine that the Valet configuration JSON file is valid. // Determine that the Valet configuration JSON file is valid.
// ================================================================================= // =================================================================================
EnvironmentCheck( EnvironmentCheck(
@@ -217,8 +229,19 @@ class Startup {
EnvironmentCheck( EnvironmentCheck(
command: { command: {
let output = valet("--version", sudo: false) let output = valet("--version", sudo: false)
// Failure condition #1: does not contain Laravel Valet
if !output.contains("Laravel Valet") {
return true
}
// Failure condition #2: version cannot be parsed
let versionString = output
.trimmingCharacters(in: .whitespacesAndNewlines)
.components(separatedBy: "Laravel Valet")[1]
.trimmingCharacters(in: .whitespaces)
// Extract the version number
Valet.shared.version = VersionExtractor.from(output) Valet.shared.version = VersionExtractor.from(output)
return Valet.shared.version == nil && output.contains("Laravel Valet") // Get the actual version
return Valet.shared.version == nil
}, },
name: "`valet --version` was loaded", name: "`valet --version` was loaded",
titleText: "startup.errors.valet_version_unknown.title".localized, titleText: "startup.errors.valet_version_unknown.title".localized,
@@ -226,42 +249,4 @@ class Startup {
descriptionText: "startup.errors.valet_version_unknown.desc".localized descriptionText: "startup.errors.valet_version_unknown.desc".localized
) )
] ]
// MARK: - EnvironmentCheck struct
/**
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
Checks that require an app restart will always lead to an alert and app termination shortly after.
*/
struct EnvironmentCheck {
let command: () async -> Bool
let name: String
let titleText: String
let subtitleText: String
let descriptionText: String
let buttonText: String
let requiresAppRestart: Bool
init(
command: @escaping () async -> Bool,
name: String,
titleText: String,
subtitleText: String,
descriptionText: String = "",
buttonText: String = "OK",
requiresAppRestart: Bool = false
) {
self.command = command
self.name = name
self.titleText = titleText
self.subtitleText = subtitleText
self.descriptionText = descriptionText
self.buttonText = buttonText
self.requiresAppRestart = requiresAppRestart
}
public func succeeds() async -> Bool {
return await !self.command()
}
}
} }

View File

@@ -55,7 +55,7 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
let path = pathControl.url!.path let path = pathControl.url!.path
let name = inputDomainName.stringValue let name = inputDomainName.stringValue
if !FileManager.default.fileExists(atPath: path) { if !Filesystem.exists(path) {
Alert.confirm( Alert.confirm(
onWindow: view.window!, onWindow: view.window!,
messageText: "domain_list.alert.folder_missing.title".localized, messageText: "domain_list.alert.folder_missing.title".localized,

View File

@@ -42,14 +42,15 @@ extension DomainListVC {
addDisabledIsolation(to: menu) addDisabledIsolation(to: menu)
} }
addUnlink(to: menu, with: site) menu.addItem(HeaderView.asMenuItem(text: "domain_list.actions".localized))
addToggleSecure(to: menu, secured: site.secured) addToggleSecure(to: menu, secured: site.secured)
addUnlink(to: menu, with: site)
tableView.menu = menu tableView.menu = menu
} }
private func addSystemApps(to menu: NSMenu) { private func addSystemApps(to menu: NSMenu) {
menu.addItem(withTitle: "domain_list.system_apps".localized, action: nil, keyEquivalent: "") menu.addItem(HeaderView.asMenuItem(text: "domain_list.system_apps".localized))
menu.addItem( menu.addItem(
withTitle: "domain_list.open_in_finder".localized, withTitle: "domain_list.open_in_finder".localized,
action: #selector(self.openInFinder), action: #selector(self.openInFinder),
@@ -70,7 +71,7 @@ extension DomainListVC {
private func addDetectedApps(to menu: NSMenu) { private func addDetectedApps(to menu: NSMenu) {
if !applications.isEmpty { if !applications.isEmpty {
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "domain_list.detected_apps".localized, action: nil, keyEquivalent: "") menu.addItem(HeaderView.asMenuItem(text: "domain_list.detected_apps".localized))
for editor in applications { for editor in applications {
let editorMenuItem = EditorMenuItem( let editorMenuItem = EditorMenuItem(
@@ -96,38 +97,40 @@ extension DomainListVC {
} }
private func addDisabledIsolation(to menu: NSMenu) { private func addDisabledIsolation(to menu: NSMenu) {
menu.addItem(HeaderView.asMenuItem(text: "domain_list.site_isolation".localized))
menu.addItem(withTitle: "domain_list.isolation_unavailable".localized, action: nil, keyEquivalent: "") menu.addItem(withTitle: "domain_list.isolation_unavailable".localized, action: nil, keyEquivalent: "")
menu.addItem(NSMenuItem.separator()) menu.addItem(NSMenuItem.separator())
} }
private func addIsolate(to menu: NSMenu, with site: ValetSite) { private func addIsolate(to menu: NSMenu, with site: ValetSite) {
if site.isolatedPhpVersion == nil { var items: [NSMenuItem] = []
// ISOLATION POSSIBLE
let isolationMenuItem = NSMenuItem(title: "domain_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) for version in PhpEnv.shared.availablePhpVersions.reversed() {
menu.addItem(NSMenuItem.separator()) let item = PhpMenuItem(
} else { title: "domain_list.always_use_php".localized(version),
// REMOVE ISOLATION POSSIBLE action: #selector(self.isolateSite),
menu.addItem(
withTitle: "domain_list.remove_isolation".localized,
action: #selector(self.removeIsolatedSite),
keyEquivalent: "" keyEquivalent: ""
) )
menu.addItem(NSMenuItem.separator()) if site.servingPhpVersion == version && site.isolatedPhpVersion != nil {
item.state = .on
item.action = nil
}
item.version = version
items.append(item)
} }
// Add the option to remove site isolation
if site.isolatedPhpVersion != nil {
items.append(NSMenuItem.separator())
items.append(NSMenuItem(
title: "domain_list.remove_isolation".localized,
action: #selector(self.removeIsolatedSite)
))
}
menu.addItem(HeaderView.asMenuItem(text: "domain_list.site_isolation".localized))
menu.addItem(NSMenuItem(title: "domain_list.isolate".localized, submenu: items))
menu.addItem(NSMenuItem.separator())
} }
private func addToggleSecure(to menu: NSMenu, secured: Bool) { private func addToggleSecure(to menu: NSMenu, secured: Bool) {

View File

@@ -13,7 +13,7 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource
// MARK: - Outlets // MARK: - Outlets
@IBOutlet weak var tableView: NSTableView! @IBOutlet weak var tableView: PMTableView!
@IBOutlet weak var progressIndicator: NSProgressIndicator! @IBOutlet weak var progressIndicator: NSProgressIndicator!
// MARK: - Variables // MARK: - Variables
@@ -64,17 +64,16 @@ class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource
let windowController = storyboard.instantiateController( let windowController = storyboard.instantiateController(
withIdentifier: "domainListWindow" withIdentifier: "domainListWindow"
) as! DomainListWC ) as! DomainListWindowController
windowController.window!.title = "domain_list.title".localized guard let window = windowController.window else { return }
windowController.window!.subtitle = "domain_list.subtitle".localized
windowController.window!.delegate = delegate window.title = "domain_list.title".localized
windowController.window!.styleMask = [ window.subtitle = "domain_list.subtitle".localized
.titled, .closable, .resizable, .miniaturizable window.delegate = delegate ?? windowController
] window.styleMask = [.titled, .closable, .resizable, .miniaturizable]
windowController.window!.minSize = NSSize(width: 550, height: 200) window.minSize = NSSize(width: 550, height: 200)
windowController.window!.delegate = windowController window.setFrameAutosaveName("domainListWindow")
windowController.window!.setFrameAutosaveName("domainListWindow")
App.shared.domainListWindowController = windowController App.shared.domainListWindowController = windowController
} }

View File

@@ -1,5 +1,5 @@
// //
// DomainListWC.swift // DomainListWindowController.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 03/12/2021. // Created by Nico Verbruggen on 03/12/2021.
@@ -8,7 +8,7 @@
import Cocoa import Cocoa
class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate { class DomainListWindowController: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
// MARK: - Window Identifier // MARK: - Window Identifier
@@ -124,8 +124,6 @@ class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate
withIdentifier: "addProxyWindow" withIdentifier: "addProxyWindow"
) as! NSWindowController ) as! NSWindowController
// let viewController = windowController.window!.contentViewController as! AddSiteVC
self.window?.beginSheet(windowController.window!) self.window?.beginSheet(windowController.window!)
} }
} }

View File

@@ -0,0 +1,27 @@
//
// PMTableView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 05/09/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Cocoa
/**
This subclassed version of NSTableView selects a row upon right-clicking,
thus making the domain list behave more like you'd expect.
*/
public class PMTableView: NSTableView {
override open func menu(for event: NSEvent) -> NSMenu? {
let row = self.row(at: self.convert(event.locationInWindow, from: nil))
if row >= 0 {
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
return super.menu(for: event)
}
}

View File

@@ -11,7 +11,7 @@ import Cocoa
class SelectionVC: NSViewController { class SelectionVC: NSViewController {
weak var domainListWC: DomainListWC? weak var domainListWC: DomainListWindowController?
@IBOutlet weak var textFieldTitle: NSTextField! @IBOutlet weak var textFieldTitle: NSTextField!
@IBOutlet weak var textFieldDescription: NSTextField! @IBOutlet weak var textFieldDescription: NSTextField!

View File

@@ -13,7 +13,7 @@ class ComposerWindow {
private var menu: MainMenu? private var menu: MainMenu?
private var shouldNotify: Bool! = nil private var shouldNotify: Bool! = nil
private var completion: ((Bool) -> Void)! = nil private var completion: ((Bool) -> Void)! = nil
private var window: ProgressWindowController? private var window: TerminalProgressWindowController?
/** /**
Updates the global dependencies and runs the completion callback when done. Updates the global dependencies and runs the completion callback when done.
@@ -25,7 +25,9 @@ class ComposerWindow {
Paths.shared.detectBinaryPaths() Paths.shared.detectBinaryPaths()
if Paths.composer == nil { if Paths.composer == nil {
presentMissingAlert() DispatchQueue.main.async {
self.presentMissingAlert()
}
return return
} }
@@ -33,7 +35,7 @@ class ComposerWindow {
menu?.setBusyImage() menu?.setBusyImage()
menu?.rebuild() menu?.rebuild()
window = ProgressWindowController.display( window = TerminalProgressWindowController.display(
title: "alert.composer_progress.title".localized, title: "alert.composer_progress.title".localized,
description: "alert.composer_progress.info".localized description: "alert.composer_progress.info".localized
) )
@@ -116,7 +118,7 @@ class ComposerWindow {
// MARK: Alert // MARK: Alert
private func presentMissingAlert() { @MainActor private func presentMissingAlert() {
BetterAlert() BetterAlert()
.withInformation( .withInformation(
title: "alert.composer_missing.title".localized, title: "alert.composer_missing.title".localized,

View File

@@ -71,7 +71,7 @@ struct PhpFrameworks {
public static func detectFallbackDependency(_ basePath: String) -> String? { public static func detectFallbackDependency(_ basePath: String) -> String? {
for entry in Self.FileMapping { for entry in Self.FileMapping {
let found = entry.value let found = entry.value
.map { path in return Filesystem.fileExists(basePath + path) } .map { path in return Filesystem.exists(basePath + path) }
.contains(true) .contains(true)
if found { if found {

View File

@@ -21,18 +21,12 @@ class NginxConfigurationFile: CreatedFromFile {
/** Resolves an nginx configuration file (.conf) */ /** Resolves an nginx configuration file (.conf) */
static func from(filePath: String) -> Self? { static func from(filePath: String) -> Self? {
let path = filePath.replacingOccurrences( let path = filePath.replacingOccurrences(of: "~", with: Paths.homePath)
of: "~",
with: "/Users/\(Paths.whoami)"
)
do { do {
let fileContents = try String(contentsOfFile: path) let fileContents = try String(contentsOfFile: path)
return Self.init( return Self.init(path: path, contents: fileContents)
path: path,
contents: fileContents
)
} catch { } catch {
Log.warn("Could not read the nginx configuration file at: `\(filePath)`") Log.warn("Could not read the nginx configuration file at: `\(filePath)`")
return nil return nil

View File

@@ -20,7 +20,7 @@ class ValetSite: DomainListable {
/// replacing the user's home folder with ~. /// replacing the user's home folder with ~.
lazy var absolutePathRelative: String = { lazy var absolutePathRelative: String = {
return self.absolutePath return self.absolutePath
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~") .replacingOccurrences(of: Paths.homePath, with: "~")
}() }()
/// The TLD used to locate this site. /// The TLD used to locate this site.

View File

@@ -53,13 +53,11 @@ extension MainMenu {
Actions.restartPhpFpm() Actions.restartPhpFpm()
Actions.restartNginx() Actions.restartNginx()
} success: { } success: {
DispatchQueue.main.async { LocalNotification.send(
LocalNotification.send( title: "notification.services_restarted".localized,
title: "notification.services_restarted".localized, subtitle: "notification.services_restarted_desc".localized,
subtitle: "notification.services_restarted_desc".localized, preference: .notifyAboutServices
preference: .notifyAboutServices )
)
}
} }
} }
@@ -67,13 +65,11 @@ extension MainMenu {
asyncExecution { asyncExecution {
Actions.stopValetServices() Actions.stopValetServices()
} success: { } success: {
DispatchQueue.main.async { LocalNotification.send(
LocalNotification.send( title: "notification.services_stopped".localized,
title: "notification.services_stopped".localized, subtitle: "notification.services_stopped_desc".localized,
subtitle: "notification.services_stopped_desc".localized, preference: .notifyAboutServices
preference: .notifyAboutServices )
)
}
} }
} }
@@ -155,7 +151,7 @@ extension MainMenu {
} }
} }
@objc func rollbackPreset() { @MainActor @objc func rollbackPreset() {
guard let preset = PresetHelper.rollbackPreset else { guard let preset = PresetHelper.rollbackPreset else {
return return
} }
@@ -180,7 +176,7 @@ extension MainMenu {
} }
} }
@objc func showPresetHelp() { @MainActor @objc func showPresetHelp() {
BetterAlert().withInformation( BetterAlert().withInformation(
title: "preset_help_title".localized, title: "preset_help_title".localized,
subtitle: "preset_help_info".localized, subtitle: "preset_help_info".localized,

View File

@@ -36,8 +36,8 @@ extension MainMenu {
*/ */
func asyncExecution( func asyncExecution(
_ execute: @escaping () throws -> Void, _ execute: @escaping () throws -> Void,
success: @escaping () -> Void = {}, success: @MainActor @escaping () -> Void = {},
failure: @escaping (Error) -> Void = { _ in }, failure: @MainActor @escaping (Error) -> Void = { _ in },
behaviours: [AsyncBehaviour] = [ behaviours: [AsyncBehaviour] = [
.setsBusyUI, .setsBusyUI,
.reloadsPhpInstallation, .reloadsPhpInstallation,
@@ -77,7 +77,11 @@ extension MainMenu {
ServicesManager.shared.loadData() ServicesManager.shared.loadData()
} }
error == nil ? success() : failure(error!) if error != nil {
return failure(error!)
}
success()
} }
} }
} }

View File

@@ -11,7 +11,7 @@ import AppKit
extension MainMenu { extension MainMenu {
@objc func fixMyValet() { @MainActor @objc func fixMyValet() {
let previousVersion = PhpEnv.phpInstall.version.short let previousVersion = PhpEnv.phpInstall.version.short
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) { if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
@@ -42,7 +42,7 @@ extension MainMenu {
} }
} }
private func presentAlertForMissingFormula() { @MainActor private func presentAlertForMissingFormula() {
BetterAlert() BetterAlert()
.withInformation( .withInformation(
title: "alert.php_formula_missing.title".localized, title: "alert.php_formula_missing.title".localized,
@@ -52,7 +52,7 @@ extension MainMenu {
.show() .show()
} }
private func presentAlertForSameVersion() { @MainActor private func presentAlertForSameVersion() {
BetterAlert() BetterAlert()
.withInformation( .withInformation(
title: "alert.fix_my_valet_done.title".localized, title: "alert.fix_my_valet_done.title".localized,
@@ -63,7 +63,7 @@ extension MainMenu {
.show() .show()
} }
private func presentAlertForDifferentVersion(version: String) { @MainActor private func presentAlertForDifferentVersion(version: String) {
BetterAlert() BetterAlert()
.withInformation( .withInformation(
title: "alert.fix_my_valet_done.title".localized, title: "alert.fix_my_valet_done.title".localized,

View File

@@ -57,6 +57,9 @@ extension MainMenu {
let installation = PhpEnv.phpInstall let installation = PhpEnv.phpInstall
installation.notifyAboutBrokenPhpFpm() installation.notifyAboutBrokenPhpFpm()
// Check for other problems
WarningManager.shared.evaluateWarnings()
// Set up the config watchers on launch (updated automatically when switching) // Set up the config watchers on launch (updated automatically when switching)
Log.info("Setting up watchers...") Log.info("Setting up watchers...")
App.shared.handlePhpConfigWatcher() App.shared.handlePhpConfigWatcher()
@@ -79,6 +82,7 @@ extension MainMenu {
// A non-default TLD is not officially supported since Valet 3.2.x // A non-default TLD is not officially supported since Valet 3.2.x
Valet.notifyAboutUnsupportedTLD() Valet.notifyAboutUnsupportedTLD()
// Find out which services are active
ServicesManager.shared.loadData() ServicesManager.shared.loadData()
// Start the background refresh timer // Start the background refresh timer
@@ -88,6 +92,14 @@ extension MainMenu {
Stats.incrementSuccessfulLaunchCount() Stats.incrementSuccessfulLaunchCount()
Stats.evaluateSponsorMessageShouldBeDisplayed() Stats.evaluateSponsorMessageShouldBeDisplayed()
// Present first launch screen if needed
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
Log.info("Should present the first launch screen!")
DispatchQueue.main.async {
OnboardingWindowController.show()
}
}
// Check for updates // Check for updates
DispatchQueue.global(qos: .utility).async { DispatchQueue.global(qos: .utility).async {
AppUpdateChecker.checkIfNewerVersionIsAvailable() AppUpdateChecker.checkIfNewerVersionIsAvailable()
@@ -143,11 +155,11 @@ extension MainMenu {
App.shared.detectedApplications = Application.detectPresetApplications() App.shared.detectedApplications = Application.detectPresetApplications()
let customApps = Preferences.custom.scanApps.map { appName in let customApps = Preferences.custom.scanApps?.map { appName in
return Application(appName, .user_supplied) return Application(appName, .user_supplied)
}.filter { app in }.filter { app in
return app.isInstalled() return app.isInstalled()
} } ?? []
App.shared.detectedApplications.append(contentsOf: customApps) App.shared.detectedApplications.append(contentsOf: customApps)

View File

@@ -52,14 +52,14 @@ extension MainMenu {
} }
} }
private func checkForPlatformIssues() { @MainActor private func checkForPlatformIssues() {
if Valet.shared.hasPlatformIssues() { if Valet.shared.hasPlatformIssues() {
Log.info("Composer platform issue(s) detected.") Log.info("Composer platform issue(s) detected.")
self.suggestFixMyComposer() self.suggestFixMyComposer()
} }
} }
private func suggestFixMyValet(failed version: String) { @MainActor private func suggestFixMyValet(failed version: String) {
let outcome = BetterAlert() let outcome = BetterAlert()
.withInformation( .withInformation(
title: "alert.php_switch_failed.title".localized(version), title: "alert.php_switch_failed.title".localized(version),
@@ -74,7 +74,7 @@ extension MainMenu {
} }
} }
private func suggestFixMyComposer() { @MainActor private func suggestFixMyComposer() {
BetterAlert().withInformation( BetterAlert().withInformation(
title: "alert.global_composer_platform_issues.title".localized, title: "alert.global_composer_platform_issues.title".localized,
subtitle: "alert.global_composer_platform_issues.subtitle".localized, subtitle: "alert.global_composer_platform_issues.subtitle".localized,
@@ -111,12 +111,14 @@ extension MainMenu {
} }
private func notifyAboutVersionChange(to version: String) { private func notifyAboutVersionChange(to version: String) {
LocalNotification.send( DispatchQueue.main.async {
title: String(format: "notification.version_changed_title".localized, version), LocalNotification.send(
subtitle: String(format: "notification.version_changed_desc".localized, version), title: String(format: "notification.version_changed_title".localized, version),
preference: .notifyAboutVersionChange subtitle: String(format: "notification.version_changed_desc".localized, version),
) preference: .notifyAboutVersionChange
)
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
}
} }
} }

View File

@@ -21,7 +21,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
/** /**
The status bar item with variable length. The status bar item with variable length.
*/ */
let statusItem = NSStatusBar.system.statusItem( @MainActor let statusItem = NSStatusBar.system.statusItem(
withLength: NSStatusItem.variableLength withLength: NSStatusItem.variableLength
) )
@@ -51,25 +51,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
*/ */
private func rebuildMenu() { private func rebuildMenu() {
let menu = StatusMenu() let menu = StatusMenu()
menu.addMenuItems()
menu.addPhpVersionMenuItems()
menu.addItem(NSMenuItem.separator())
menu.addPhpActionMenuItems()
menu.addItem(NSMenuItem.separator())
menu.addValetMenuItems()
menu.addItem(NSMenuItem.separator())
menu.addRemainingMenuItems()
menu.addItem(NSMenuItem.separator())
menu.addCoreMenuItems()
menu.items.forEach({ (item) in menu.items.forEach({ (item) in
item.target = self item.target = self
}) })
statusItem.menu = menu statusItem.menu = menu
statusItem.menu?.delegate = self statusItem.menu?.delegate = self
} }
@@ -77,7 +62,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
/** /**
Sets the status bar image based on a version string. Sets the status bar image based on a version string.
*/ */
func setStatusBarImage(version: String) { @MainActor func setStatusBarImage(version: String) {
setStatusBar( setStatusBar(
image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue) image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue)
? MenuBarImageGenerator.textToImageWithIcon(text: version) ? MenuBarImageGenerator.textToImageWithIcon(text: version)
@@ -89,7 +74,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
Sets the status bar image, based on the provided NSImage. Sets the status bar image, based on the provided NSImage.
The image will be used as a template image. The image will be used as a template image.
*/ */
func setStatusBar(image: NSImage) { @MainActor func setStatusBar(image: NSImage) {
if let button = statusItem.button { if let button = statusItem.button {
image.isTemplate = true image.isTemplate = true
button.image = image button.image = image
@@ -125,6 +110,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
ServicesManager.shared.loadData() ServicesManager.shared.loadData()
} }
/**
Shows the Welcome Tour screen, again.
Did this need a comment? No, probably not.
*/
@objc func showWelcomeTour() {
DispatchQueue.main.async {
OnboardingWindowController.show()
}
}
/** Reloads the menu in the background, using `asyncExecution`. */ /** Reloads the menu in the background, using `asyncExecution`. */
@objc func reloadPhpMonitorMenuInBackground() { @objc func reloadPhpMonitorMenuInBackground() {
asyncExecution({ asyncExecution({
@@ -171,7 +166,11 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
} }
@objc func openPrefs() { @objc func openPrefs() {
PrefsWC.show() PreferencesWindowController.show()
}
@objc func openWarnings() {
WarningsWindowController.show()
} }
@objc func openDomainList() { @objc func openDomainList() {

View File

@@ -2,72 +2,176 @@
// StatusMenu+Items.swift // StatusMenu+Items.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 30/05/2022. // Created by Nico Verbruggen on 18/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved. // Copyright © 2022 Nico Verbruggen. All rights reserved.
// //
import Cocoa import Cocoa
// MARK: - PHP Version
extension StatusMenu { extension StatusMenu {
// MARK: Remaining Menu Items func addPhpVersionMenuItems() {
if PhpEnv.phpInstall.version.error {
let brokenMenuItems = ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"]
return addItems(brokenMenuItems.map { NSMenuItem(title: $0.localized) })
}
addItem(HeaderView.asMenuItem(
text: "\("mi_php_version".localized) \(PhpEnv.phpInstall.version.long)",
minimumWidth: 280 // this ensures the menu is at least wide enough not to cause clipping
))
}
func addPhpActionMenuItems() {
if PhpEnv.shared.isBusy {
addItem(NSMenuItem(title: "mi_busy".localized))
return
}
if PhpEnv.shared.availablePhpVersions.isEmpty { return }
addSwitchToPhpMenuItems()
self.addItem(NSMenuItem.separator())
}
func addServicesManagerMenuItem() {
if PhpEnv.shared.isBusy {
return
}
addItems([
ServicesView.asMenuItem(),
NSMenuItem.separator()
])
}
func addSwitchToPhpMenuItems() {
var shortcutKey = 1
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
// Get the short and long version
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.versionNumber
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
let versionString = long ? longVersion.toString() : shortVersion
let action = #selector(MainMenu.switchToPhpVersion(sender:))
let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)"
let menuItem = PhpMenuItem(
title: "\("mi_php_switch".localized) \(versionString) (\(brew))",
action: (shortVersion == PhpEnv.phpInstall.version.short)
? nil
: action, keyEquivalent: "\(shortcutKey)"
)
menuItem.version = shortVersion
shortcutKey += 1
addItem(menuItem)
}
}
func addCoreMenuItems() {
addItems([
NSMenuItem.separator(),
NSMenuItem(title: "mi_preferences".localized,
action: #selector(MainMenu.openPrefs), keyEquivalent: ","),
NSMenuItem(title: "mi_check_for_updates".localized,
action: #selector(MainMenu.checkForUpdates)),
NSMenuItem.separator(),
NSMenuItem(title: "mi_about".localized,
action: #selector(MainMenu.openAbout)),
NSMenuItem(title: "mi_quit".localized,
action: #selector(MainMenu.terminateApp), keyEquivalent: "q")
])
}
// MARK: - Valet
func addValetMenuItems() {
addItems([
HeaderView.asMenuItem(text: "mi_valet".localized),
NSMenuItem(title: "mi_valet_config".localized,
action: #selector(MainMenu.openValetConfigFolder),
keyEquivalent: "v"),
NSMenuItem(title: "mi_domain_list".localized,
action: #selector(MainMenu.openDomainList),
keyEquivalent: "l"),
NSMenuItem.separator()
])
}
// MARK: - PHP Configuration
func addConfigurationMenuItems() { func addConfigurationMenuItems() {
self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized)) addItems([
self.addItem( HeaderView.asMenuItem(text: "mi_configuration".localized),
NSMenuItem(title: "mi_php_config".localized, NSMenuItem(title: "mi_php_config".localized,
action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c") action: #selector(MainMenu.openActiveConfigFolder),
) keyEquivalent: "c"),
self.addItem(
NSMenuItem(title: "mi_phpmon_config".localized, NSMenuItem(title: "mi_phpmon_config".localized,
action: #selector(MainMenu.openPhpMonitorConfigurationFile), keyEquivalent: "y") action: #selector(MainMenu.openPhpMonitorConfigurationFile),
) keyEquivalent: "y"),
self.addItem( NSMenuItem(title: "mi_phpinfo".localized,
NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i") action: #selector(MainMenu.openPhpInfo),
) keyEquivalent: "i")
])
} }
// MARK: - Composer
func addComposerMenuItems() { func addComposerMenuItems() {
self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) addItems([
self.addItem( HeaderView.asMenuItem(text: "mi_composer".localized),
NSMenuItem(title: "mi_global_composer".localized, NSMenuItem(
action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g") title: "mi_global_composer".localized,
) action: #selector(MainMenu.openGlobalComposerFolder),
keyEquivalent: "g"
let composerMenuItem = NSMenuItem( ),
title: "mi_update_global_composer".localized, NSMenuItem(
action: PhpEnv.shared.isBusy ? nil : #selector(MainMenu.updateGlobalComposerDependencies), title: "mi_update_global_composer".localized,
keyEquivalent: "g" action: PhpEnv.shared.isBusy
) ? nil
composerMenuItem.keyEquivalentModifierMask = .shift : #selector(MainMenu.updateGlobalComposerDependencies),
keyEquivalent: "g",
self.addItem(composerMenuItem) keyModifier: [.shift]
)
])
} }
// MARK: - Stats
func addStatsMenuItem() { func addStatsMenuItem() {
guard let stats = PhpEnv.phpInstall.limits else { return } guard let stats = PhpEnv.phpInstall.limits else { return }
self.addItem(StatsView.asMenuItem( addItem(StatsView.asMenuItem(
memory: stats.memory_limit, memory: stats.memory_limit,
post: stats.post_max_size, post: stats.post_max_size,
upload: stats.upload_max_filesize) upload: stats.upload_max_filesize)
) )
} }
// MARK: - Extensions
func addExtensionsMenuItems() { func addExtensionsMenuItems() {
self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized)) addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized))
if PhpEnv.phpInstall.extensions.isEmpty { if PhpEnv.phpInstall.extensions.isEmpty {
self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: ""))
} }
var shortcutKey = 1 var shortcutKey = 1
for phpExtension in PhpEnv.phpInstall.extensions { for phpExtension in PhpEnv.phpInstall.extensions {
self.addExtensionItem(phpExtension, shortcutKey) addExtensionItem(phpExtension, shortcutKey)
shortcutKey += 1 shortcutKey += 1
} }
} }
// MARK: - Presets
func addPresetsMenuItem() { func addPresetsMenuItem() {
guard let presets = Preferences.custom.presets else { guard let presets = Preferences.custom.presets else {
addEmptyPresetHelp() addEmptyPresetHelp()
@@ -82,176 +186,126 @@ extension StatusMenu {
addLoadedPresets() addLoadedPresets()
} }
func addEmptyPresetHelp() { private func addEmptyPresetHelp() {
let presets = NSMenuItem(title: "mi_presets_title".localized, action: nil, keyEquivalent: "") addItem(NSMenuItem(title: "mi_presets_title".localized, submenu: [
NSMenuItem(title: "mi_no_presets".localized),
let presetsMenu = NSMenu() NSMenuItem.separator(),
NSMenuItem(title: "mi_set_up_presets".localized,
presetsMenu.addItem(NSMenuItem(title: "mi_no_presets".localized, action: nil, keyEquivalent: "")) action: #selector(MainMenu.showPresetHelp))
presetsMenu.addItem(NSMenuItem.separator()) ], target: MainMenu.shared))
presetsMenu.addItem(NSMenuItem(
title: "mi_set_up_presets".localized,
action: #selector(MainMenu.showPresetHelp),
keyEquivalent: "")
)
presetsMenu.items.forEach { $0.target = MainMenu.shared }
self.setSubmenu(presetsMenu, for: presets)
self.addItem(presets)
return
} }
func addLoadedPresets() { private func addLoadedPresets() {
let presets = NSMenuItem(title: "mi_presets_title".localized, action: nil, keyEquivalent: "") addItem(NSMenuItem(title: "mi_presets_title".localized, submenu: [
NSMenuItem.separator(),
let presetsMenu = NSMenu() HeaderView.asMenuItem(text: "mi_apply_presets_title".localized)
] + PresetMenuItem.getAll() + [
presetsMenu.addItem(NSMenuItem.separator()) NSMenuItem.separator(),
presetsMenu.addItem(HeaderView.asMenuItem(text: "mi_apply_presets_title".localized)) NSMenuItem(title: "mi_revert_to_prev_config".localized,
action: PresetHelper.rollbackPreset != nil ? #selector(MainMenu.rollbackPreset) : nil),
for preset in Preferences.custom.presets! { NSMenuItem.separator(),
let presetMenuItem = PresetMenuItem( NSMenuItem(title: "mi_profiles_loaded".localized(Preferences.custom.presets!.count))
title: preset.getMenuItemText(), ], target: MainMenu.shared))
action: #selector(MainMenu.togglePreset(sender:)),
keyEquivalent: ""
)
if let attributedString = try? NSMutableAttributedString(
data: preset.getMenuItemText().data(using: .utf8)!,
options: [.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil
) {
presetMenuItem.attributedTitle = attributedString
}
presetMenuItem.preset = preset
presetsMenu.addItem(presetMenuItem)
}
presetsMenu.addItem(NSMenuItem.separator())
presetsMenu.addItem(NSMenuItem(
title: "mi_revert_to_prev_config".localized,
action: PresetHelper.rollbackPreset != nil
? #selector(MainMenu.rollbackPreset)
: nil,
keyEquivalent: ""
))
presetsMenu.addItem(NSMenuItem.separator())
presetsMenu.addItem(NSMenuItem(
title: "mi_profiles_loaded".localized(
Preferences.custom.presets!.count
),
action: nil, keyEquivalent: "")
)
for item in presetsMenu.items {
item.target = MainMenu.shared
}
self.setSubmenu(presetsMenu, for: presets)
self.addItem(presets)
} }
// MARK: - Xdebug
func addXdebugMenuItem() { func addXdebugMenuItem() {
if !Xdebug.enabled { if !Xdebug.enabled {
return return
} }
let xdebugSwitch = NSMenuItem( addItems([
title: "mi_xdebug_mode".localized, NSMenuItem(title: "mi_xdebug_mode".localized, submenu: [
action: nil, HeaderView.asMenuItem(text: "mi_xdebug_available_modes".localized)
keyEquivalent: "" ] + Xdebug.asMenuItems() + [
) HeaderView.asMenuItem(text: "mi_xdebug_actions".localized),
let xdebugModesMenu = NSMenu() NSMenuItem(title: "mi_xdebug_disable_all".localized,
let activeModes = Xdebug.activeModes action: #selector(MainMenu.disableAllXdebugModes))
], target: MainMenu.shared),
xdebugModesMenu.addItem(HeaderView.asMenuItem(text: "mi_xdebug_available_modes".localized)) NSMenuItem.separator()
], target: MainMenu.shared)
for mode in Xdebug.modes {
let item = XdebugMenuItem(
title: mode,
action: #selector(MainMenu.toggleXdebugMode(sender:)),
keyEquivalent: ""
)
item.state = activeModes.contains(mode) ? .on : .off
item.mode = mode
xdebugModesMenu.addItem(item)
}
xdebugModesMenu.addItem(HeaderView.asMenuItem(text: "mi_xdebug_actions".localized))
xdebugModesMenu.addItem(
withTitle: "mi_xdebug_disable_all".localized,
action: #selector(MainMenu.disableAllXdebugModes),
keyEquivalent: ""
)
for item in xdebugModesMenu.items {
item.target = MainMenu.shared
}
self.setSubmenu(xdebugModesMenu, for: xdebugSwitch)
self.addItem(xdebugSwitch)
} }
// MARK: - PHP Doctor
func addPhpDoctorMenuItem() {
if !Preferences.isEnabled(.showPhpDoctorSuggestions) ||
!WarningManager.shared.hasWarnings() {
return
}
addItems([
HeaderView.asMenuItem(text: "mi_php_doctor".localized),
NSMenuItem(title: "mi_recommendations_count".localized(WarningManager.shared.warnings.count)),
NSMenuItem(title: "mi_view_recommendations".localized, action: #selector(MainMenu.openWarnings)),
NSMenuItem.separator()
])
}
// MARK: - First Aid & Services
func addFirstAidAndServicesMenuItems() { func addFirstAidAndServicesMenuItems() {
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "") let services = NSMenuItem(title: "mi_other".localized)
let servicesMenu = NSMenu() let servicesMenu = NSMenu()
servicesMenu.addItems([
// FIRST AID
HeaderView.asMenuItem(text: "mi_first_aid".localized),
NSMenuItem(title: "mi_view_onboarding".localized, action: #selector(MainMenu.showWelcomeTour)),
NSMenuItem(title: "mi_fa_php_doctor".localized, action: #selector(MainMenu.openWarnings)),
NSMenuItem.separator(),
NSMenuItem(title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
action: #selector(MainMenu.fixMyValet),
toolTip: "mi_fix_my_valet_tooltip".localized),
NSMenuItem(title: "mi_fix_brew_permissions".localized(), action: #selector(MainMenu.fixHomebrewPermissions),
toolTip: "mi_fix_brew_permissions_tooltip".localized),
NSMenuItem.separator(),
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized)) // SERVICES
let fixMyValetMenuItem = NSMenuItem( HeaderView.asMenuItem(text: "mi_services".localized),
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion), NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq),
action: #selector(MainMenu.fixMyValet), keyEquivalent: "" keyEquivalent: "d"),
) NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm),
fixMyValetMenuItem.toolTip = "mi_fix_my_valet_tooltip".localized keyEquivalent: "p"),
servicesMenu.addItem(fixMyValetMenuItem) NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx),
keyEquivalent: "n"),
NSMenuItem(title: "mi_restart_valet_services".localized, action: #selector(MainMenu.restartValetServices),
keyEquivalent: "s"),
NSMenuItem(title: "mi_stop_valet_services".localized, action: #selector(MainMenu.stopValetServices),
keyEquivalent: "s",
keyModifier: [.command, .shift]),
NSMenuItem.separator(),
let fixHomebrewMenuItem = NSMenuItem( // MANUAL ACTIONS
title: "mi_fix_brew_permissions".localized(), HeaderView.asMenuItem(text: "mi_manual_actions".localized),
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: ""
)
fixHomebrewMenuItem.toolTip = "mi_fix_brew_permissions_tooltip".localized
servicesMenu.addItem(fixHomebrewMenuItem)
servicesMenu.addItem(NSMenuItem.separator())
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_dnsmasq".localized,
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_php_fpm".localized,
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_nginx".localized,
action: #selector(MainMenu.restartNginx), keyEquivalent: "n")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_restart_valet_services".localized,
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s")
)
servicesMenu.addItem(
NSMenuItem(title: "mi_stop_valet_services".localized,
action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"),
withKeyModifier: [.command, .shift]
)
servicesMenu.addItem(NSMenuItem.separator())
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_manual_actions".localized))
servicesMenu.addItem(
NSMenuItem(title: "mi_php_refresh".localized, NSMenuItem(title: "mi_php_refresh".localized,
action: #selector(MainMenu.reloadPhpMonitorMenuInForeground), keyEquivalent: "r") action: #selector(MainMenu.reloadPhpMonitorMenuInForeground),
) keyEquivalent: "r")
], target: MainMenu.shared)
for item in servicesMenu.items { setSubmenu(servicesMenu, for: services)
item.target = MainMenu.shared addItem(services)
}
self.setSubmenu(servicesMenu, for: services)
self.addItem(services)
} }
// MARK: - Other helper methods to generate menu items
func addExtensionItem(_ phpExtension: PhpExtension, _ shortcutKey: Int) {
let keyEquivalent = shortcutKey < 9 ? "\(shortcutKey)" : ""
let menuItem = ExtensionMenuItem(
title: "\(phpExtension.name) (\(phpExtension.fileNameOnly))",
action: #selector(MainMenu.toggleExtension),
keyEquivalent: keyEquivalent
)
if menuItem.keyEquivalent != "" {
menuItem.keyEquivalentModifierMask = [.option]
}
menuItem.state = phpExtension.enabled ? .on : .off
menuItem.phpExtension = phpExtension
addItem(menuItem)
}
} }

View File

@@ -8,130 +8,63 @@
import Cocoa import Cocoa
class StatusMenu: NSMenu { class StatusMenu: NSMenu {
func addMenuItems() {
addPhpVersionMenuItems()
addItem(NSMenuItem.separator())
func addPhpVersionMenuItems() { if Preferences.isEnabled(.displayGlobalVersionSwitcher) {
if PhpEnv.phpInstall.version.error { addPhpActionMenuItems()
for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] { addItem(NSMenuItem.separator())
addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: ""))
}
return
} }
let phpVersionText = "\("mi_php_version".localized) \(PhpEnv.phpInstall.version.long)" if Preferences.isEnabled(.displayServicesManager) {
addItem(HeaderView.asMenuItem(text: phpVersionText)) addServicesManagerMenuItem()
} addItem(NSMenuItem.separator())
func addPhpActionMenuItems() {
if PhpEnv.shared.isBusy {
addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: ""))
return
} }
if PhpEnv.shared.availablePhpVersions.isEmpty { if Preferences.isEnabled(.displayValetIntegration) {
return addValetMenuItems()
addItem(NSMenuItem.separator())
} }
self.addSwitchToPhpMenuItems() if Preferences.isEnabled(.displayPhpConfigFinder) {
self.addItem(NSMenuItem.separator()) addConfigurationMenuItems()
addItem(NSMenuItem.separator())
}
self.addItem(ServicesView.asMenuItem()) if Preferences.isEnabled(.displayComposerToolkit) {
self.addItem(NSMenuItem.separator()) addComposerMenuItems()
} addItem(NSMenuItem.separator())
}
func addValetMenuItems() {
self.addItem(HeaderView.asMenuItem(text: "mi_valet".localized))
self.addItem(NSMenuItem(
title: "mi_valet_config".localized, action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
self.addItem(NSMenuItem(
title: "mi_domain_list".localized, action: #selector(MainMenu.openDomainList), keyEquivalent: "l"))
self.addItem(NSMenuItem.separator())
}
func addRemainingMenuItems() {
self.addConfigurationMenuItems()
self.addItem(NSMenuItem.separator())
self.addComposerMenuItems()
if PhpEnv.shared.isBusy { if PhpEnv.shared.isBusy {
return return
} }
self.addItem(NSMenuItem.separator()) if Preferences.isEnabled(.displayLimitsWidget) {
addStatsMenuItem()
self.addStatsMenuItem() addItem(NSMenuItem.separator())
self.addItem(NSMenuItem.separator())
self.addExtensionsMenuItems()
self.addItem(NSMenuItem.separator())
self.addXdebugMenuItem()
self.addPresetsMenuItem()
self.addFirstAidAndServicesMenuItems()
}
func addCoreMenuItems() {
self.addItem(NSMenuItem.separator())
self.addItem(NSMenuItem(title: "mi_preferences".localized,
action: #selector(MainMenu.openPrefs), keyEquivalent: ","))
self.addItem(NSMenuItem(title: "mi_check_for_updates".localized,
action: #selector(MainMenu.checkForUpdates), keyEquivalent: ""))
self.addItem(NSMenuItem.separator())
self.addItem(NSMenuItem(title: "mi_about".localized,
action: #selector(MainMenu.openAbout), keyEquivalent: ""))
self.addItem(NSMenuItem(title: "mi_quit".localized,
action: #selector(MainMenu.terminateApp), keyEquivalent: "q"))
}
// MARK: Private Helpers
internal func addSwitchToPhpMenuItems() {
var shortcutKey = 1
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
// Get the short and long version
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.versionNumber
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
let versionString = long ? longVersion.toString() : shortVersion
let action = #selector(MainMenu.switchToPhpVersion(sender:))
let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)"
let menuItem = PhpMenuItem(
title: "\("mi_php_switch".localized) \(versionString) (\(brew))",
action: (shortVersion == PhpEnv.phpInstall.version.short)
? nil
: action, keyEquivalent: "\(shortcutKey)"
)
menuItem.version = shortVersion
shortcutKey += 1
self.addItem(menuItem)
}
}
internal func addExtensionItem(_ phpExtension: PhpExtension, _ shortcutKey: Int) {
let keyEquivalent = shortcutKey < 9 ? "\(shortcutKey)" : ""
let menuItem = ExtensionMenuItem(
title: "\(phpExtension.name) (\(phpExtension.fileNameOnly))",
action: #selector(MainMenu.toggleExtension),
keyEquivalent: keyEquivalent
)
if menuItem.keyEquivalent != "" {
menuItem.keyEquivalentModifierMask = [.option]
} }
menuItem.state = phpExtension.enabled ? .on : .off if Preferences.isEnabled(.displayExtensions) {
menuItem.phpExtension = phpExtension addExtensionsMenuItems()
NSMenuItem.separator()
self.addItem(menuItem) addXdebugMenuItem()
}
addPhpDoctorMenuItem()
if Preferences.isEnabled(.displayPresets) {
addPresetsMenuItem()
}
if Preferences.isEnabled(.displayMisc) {
addFirstAidAndServicesMenuItems()
}
addItem(NSMenuItem.separator())
addCoreMenuItems()
} }
} }

View File

@@ -103,14 +103,14 @@ class BetterAlert {
/** /**
Shows the modal and does not return anything. Shows the modal and does not return anything.
*/ */
public func show() { @MainActor public func show() {
_ = self.runModal() _ = self.runModal()
} }
/** /**
Shows the modal for a particular error. Shows the modal for a particular error.
*/ */
public static func show(for error: Error & AlertableError) { @MainActor public static func show(for error: Error & AlertableError) {
let key = error.getErrorMessageKey() let key = error.getErrorMessageKey()
return BetterAlert().withInformation( return BetterAlert().withInformation(
title: "\(key).title".localized, title: "\(key).title".localized,

View File

@@ -0,0 +1,45 @@
//
// OnboardingWindowController.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 25/06/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Cocoa
import SwiftUI
class OnboardingWindowController: PMWindowController {
// MARK: - Window Identifier
override var windowName: String {
return "Onboarding"
}
public static func create(delegate: NSWindowDelegate?) {
let windowController = Self()
windowController.window = NSWindow()
guard let window = windowController.window else { return }
window.title = ""
window.styleMask = [.titled, .closable, .miniaturizable]
window.titlebarAppearsTransparent = true
window.delegate = delegate ?? windowController
window.contentView = NSHostingView(rootView: OnboardingView())
window.setContentSize(NSSize(width: 600, height: 600))
App.shared.onboardingWindowController = windowController
}
public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.onboardingWindowController == nil {
Self.create(delegate: delegate)
}
App.shared.onboardingWindowController?.showWindow(self)
App.shared.onboardingWindowController?.window?.setCenterPosition(offsetY: 70)
NSApp.activate(ignoringOtherApps: true)
}
}

View File

@@ -9,9 +9,10 @@
import Foundation import Foundation
struct CustomPrefs: Decodable { struct CustomPrefs: Decodable {
let scanApps: [String] let scanApps: [String]?
let presets: [Preset]? let presets: [Preset]?
let services: [String]? let services: [String]?
let environmentVariables: [String: String]?
public func hasPresets() -> Bool { public func hasPresets() -> Bool {
return self.presets != nil && !self.presets!.isEmpty return self.presets != nil && !self.presets!.isEmpty
@@ -21,9 +22,74 @@ struct CustomPrefs: Decodable {
return self.services != nil && !self.services!.isEmpty return self.services != nil && !self.services!.isEmpty
} }
public func hasEnvironmentVariables() -> Bool {
return self.environmentVariables != nil && !self.environmentVariables!.keys.isEmpty
}
public func getEnvironmentVariables() -> String {
return self.environmentVariables!.map { (key, value) in
return "export \(key)=\(value)"
}.joined(separator: "&&")
}
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case scanApps = "scan_apps" case scanApps = "scan_apps"
case presets = "presets" case presets = "presets"
case services = "services" case services = "services"
case environmentVariables = "export"
}
}
extension Preferences {
func loadCustomPreferences() {
// Ensure the configuration directory is created if missing
Shell.run("mkdir -p ~/.config/phpmon")
// Move the legacy file
moveOutdatedConfigurationFile()
// Attempt to load the file if it exists
let url = URL(fileURLWithPath: "\(Paths.homePath)/.config/phpmon/config.json")
if Filesystem.fileExists(url.path) {
Log.info("A custom ~/.config/phpmon/config.json file was found. Attempting to parse...")
loadCustomPreferencesFile(url)
} else {
Log.info("There was no /.config/phpmon/config.json file to be loaded.")
}
}
func moveOutdatedConfigurationFile() {
if Filesystem.fileExists("~/.phpmon.conf.json") && !Filesystem.fileExists("~/.config/phpmon/config.json") {
Log.info("An outdated configuration file was found. Moving it...")
Shell.run("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
Log.info("The configuration file was copied successfully!")
}
}
func loadCustomPreferencesFile(_ url: URL) {
do {
customPreferences = try JSONDecoder().decode(
CustomPrefs.self,
from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)!
)
Log.info("The ~/.config/phpmon/config.json file was successfully parsed.")
if customPreferences.hasPresets() {
Log.info("There are \(customPreferences.presets!.count) custom presets.")
}
if customPreferences.hasServices() {
Log.info("There are custom services: \(customPreferences.services!)")
}
if customPreferences.hasEnvironmentVariables() {
Log.info("Configuring the additional exports...")
Shell.user.exports = customPreferences.getEnvironmentVariables()
}
} catch {
Log.warn("The ~/.config/phpmon/config.json file seems to be missing or malformed.")
}
} }
} }

View File

@@ -0,0 +1,14 @@
//
// Keys.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 25/07/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
struct Keys {
static let Escape = 53
static let Space = 49
}

View File

@@ -0,0 +1,106 @@
//
// PreferenceName.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 07/09/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
/**
These are the keys used for every preference in the app.
*/
enum PreferenceName: String {
// FIRST-TIME LAUNCH
case wasLaunchedBefore = "launched_before"
// GENERAL
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
case allowProtocolForIntegrations = "allow_protocol_for_integrations"
case globalHotkey = "global_hotkey"
case automaticBackgroundUpdateCheck = "backgroundUpdateCheck"
case showPhpDoctorSuggestions = "show_php_doctor_suggestions"
// APPEARANCE
case shouldDisplayDynamicIcon = "use_dynamic_icon"
case iconTypeToDisplay = "icon_type_to_display"
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
// NOTIFICATIONS
case notifyAboutVersionChange = "notify_about_version_change"
case notifyAboutPhpFpmRestart = "notify_about_php_fpm_restart"
case notifyAboutServices = "notify_about_services_restart"
case notifyAboutPresets = "notify_about_presets"
case notifyAboutSecureToggle = "notify_about_secure_toggle"
case notifyAboutGlobalComposerStatus = "notify_about_composer_status"
// MENU CUSTOMIZATION
case displayGlobalVersionSwitcher = "display_global_version_switcher"
case displayServicesManager = "display_services_manager"
case displayValetIntegration = "display_valet_integration"
case displayPhpConfigFinder = "display_php_config_finder"
case displayComposerToolkit = "display_composer_toolkit"
case displayLimitsWidget = "display_limits_widget"
case displayExtensions = "display_extensions"
case displayPresets = "display_presets"
case displayMisc = "display_misc"
/**
What type of data each preference contains.
*/
static var mapping: [PreferenceType: [PreferenceName]] = [
.boolean: [
// Preferences
.shouldDisplayDynamicIcon,
.fullPhpVersionDynamicIcon,
.autoServiceRestartAfterExtensionToggle,
.autoComposerGlobalUpdateAfterSwitch,
.allowProtocolForIntegrations,
.automaticBackgroundUpdateCheck,
.showPhpDoctorSuggestions,
// Notifications
.notifyAboutVersionChange,
.notifyAboutPhpFpmRestart,
.notifyAboutServices,
.notifyAboutPresets,
.notifyAboutSecureToggle,
.notifyAboutGlobalComposerStatus,
// UI Preferences
.displayGlobalVersionSwitcher,
.displayServicesManager,
.displayValetIntegration,
.displayPhpConfigFinder,
.displayComposerToolkit,
.displayLimitsWidget,
.displayExtensions,
.displayPresets,
.displayMisc
],
.string: [
.globalHotkey,
.iconTypeToDisplay
]
]
}
enum PreferenceType {
case boolean, string
}
/**
These are retired preferences that, if present, should be migrated.
*/
enum RetiredPreferenceName: String {
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
}
/**
These are internal stats. They NEVER get shared.
*/
enum InternalStats: String {
case launchCount = "times_launched"
case switchCount = "times_switched_versions"
case didSeeSponsorEncouragement = "did_see_sponsor_encouragement"
}

View File

@@ -8,50 +8,6 @@
import Foundation import Foundation
/**
These are the keys used for every preference in the app.
*/
enum PreferenceName: String {
// FIRST-TIME LAUNCH
case wasLaunchedBefore = "launched_before"
// GENERAL
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
case allowProtocolForIntegrations = "allow_protocol_for_integrations"
case globalHotkey = "global_hotkey"
case automaticBackgroundUpdateCheck = "backgroundUpdateCheck"
// APPEARANCE
case shouldDisplayDynamicIcon = "use_dynamic_icon"
case iconTypeToDisplay = "icon_type_to_display"
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
// NOTIFICATIONS
case notifyAboutVersionChange = "notify_about_version_change"
case notifyAboutPhpFpmRestart = "notify_about_php_fpm_restart"
case notifyAboutServices = "notify_about_services_restart"
case notifyAboutPresets = "notify_about_presets"
case notifyAboutSecureToggle = "notify_about_secure_toggle"
case notifyAboutGlobalComposerStatus = "notify_about_composer_status"
}
/**
These are retired preferences that, if present, should be migrated.
*/
enum RetiredPreferenceName: String {
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
}
/**
These are internal stats. They NEVER get shared.
*/
enum InternalStats: String {
case launchCount = "times_launched"
case switchCount = "times_switched_versions"
case didSeeSponsorEncouragement = "did_see_sponsor_encouragement"
}
class Preferences { class Preferences {
// MARK: - Singleton // MARK: - Singleton
@@ -65,7 +21,12 @@ class Preferences {
public init() { public init() {
Preferences.handleFirstTimeLaunch() Preferences.handleFirstTimeLaunch()
cachedPreferences = Self.cache() cachedPreferences = Self.cache()
customPreferences = CustomPrefs(scanApps: [], presets: [], services: []) customPreferences = CustomPrefs(
scanApps: [],
presets: [],
services: [],
environmentVariables: [:]
)
loadCustomPreferences() loadCustomPreferences()
} }
@@ -88,6 +49,7 @@ class Preferences {
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false, PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
PreferenceName.allowProtocolForIntegrations.rawValue: true, PreferenceName.allowProtocolForIntegrations.rawValue: true,
PreferenceName.automaticBackgroundUpdateCheck.rawValue: true, PreferenceName.automaticBackgroundUpdateCheck.rawValue: true,
PreferenceName.showPhpDoctorSuggestions.rawValue: true,
/// Preferences: Appearance /// Preferences: Appearance
PreferenceName.shouldDisplayDynamicIcon.rawValue: true, PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
@@ -102,6 +64,17 @@ class Preferences {
PreferenceName.notifyAboutSecureToggle.rawValue: true, PreferenceName.notifyAboutSecureToggle.rawValue: true,
PreferenceName.notifyAboutGlobalComposerStatus.rawValue: true, PreferenceName.notifyAboutGlobalComposerStatus.rawValue: true,
/// Preferences: UI Preferences
PreferenceName.displayGlobalVersionSwitcher.rawValue: true,
PreferenceName.displayServicesManager.rawValue: true,
PreferenceName.displayValetIntegration.rawValue: true,
PreferenceName.displayPhpConfigFinder.rawValue: true,
PreferenceName.displayComposerToolkit.rawValue: true,
PreferenceName.displayLimitsWidget.rawValue: true,
PreferenceName.displayExtensions.rawValue: true,
PreferenceName.displayPresets.rawValue: true,
PreferenceName.displayMisc.rawValue: true,
/// Stats /// Stats
InternalStats.switchCount.rawValue: 0, InternalStats.switchCount.rawValue: 0,
InternalStats.launchCount.rawValue: 0, InternalStats.launchCount.rawValue: 0,
@@ -158,42 +131,18 @@ class Preferences {
// MARK: - Internal Functionality // MARK: - Internal Functionality
private static func cache() -> [PreferenceName: Any] { private static func cache() -> [PreferenceName: Any?] {
return [ return Dictionary(uniqueKeysWithValues: PreferenceName.mapping
// Part 1: Always Booleans .flatMap { (key: PreferenceType, value: [PreferenceName]) in
.shouldDisplayDynamicIcon: value.map { preference -> (PreferenceName, Any?) in
UserDefaults.standard.bool( return (preference, { () -> Any? in
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any, switch key {
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool( case .boolean: return UserDefaults.standard.bool(forKey: preference.rawValue)
forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any, case .string: return UserDefaults.standard.string(forKey: preference.rawValue)
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool( }
forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any, }())
.autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool( }
forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any, })
.allowProtocolForIntegrations: UserDefaults.standard.bool(
forKey: PreferenceName.allowProtocolForIntegrations.rawValue) as Any,
.automaticBackgroundUpdateCheck: UserDefaults.standard.bool(
forKey: PreferenceName.automaticBackgroundUpdateCheck.rawValue) as Any,
.notifyAboutVersionChange: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutVersionChange.rawValue) as Any,
.notifyAboutPhpFpmRestart: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutPhpFpmRestart.rawValue) as Any,
.notifyAboutServices: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutServices.rawValue) as Any,
.notifyAboutPresets: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutPresets.rawValue) as Any,
.notifyAboutSecureToggle: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutSecureToggle.rawValue) as Any,
.notifyAboutGlobalComposerStatus: UserDefaults.standard.bool(
forKey: PreferenceName.notifyAboutGlobalComposerStatus.rawValue) as Any,
// Part 2: Always Strings
.globalHotkey: UserDefaults.standard.string(
forKey: PreferenceName.globalHotkey.rawValue) as Any,
.iconTypeToDisplay: UserDefaults.standard.string(
forKey: PreferenceName.iconTypeToDisplay.rawValue) as Any
]
} }
static func update(_ preference: PreferenceName, value: Any?) { static func update(_ preference: PreferenceName, value: Any?) {
@@ -207,54 +156,4 @@ class Preferences {
// Update the preferences cache in memory! // Update the preferences cache in memory!
Preferences.shared.cachedPreferences = Preferences.cache() Preferences.shared.cachedPreferences = Preferences.cache()
} }
// MARK: - Custom Preferences
private func loadCustomPreferences() {
// Ensure the configuration directory is created if missing
Shell.run("mkdir -p ~/.config/phpmon")
// Move the legacy file
moveOutdatedConfigurationFile()
// Attempt to load the file if it exists
let url = URL(fileURLWithPath: "/Users/\(Paths.whoami)/.config/phpmon/config.json")
if Filesystem.fileExists(url.path) {
Log.info("A custom ~/.config/phpmon/config.json file was found. Attempting to parse...")
loadCustomPreferencesFile(url)
} else {
Log.info("There was no /.config/phpmon/config.json file to be loaded.")
}
}
private func moveOutdatedConfigurationFile() {
if Filesystem.fileExists("~/.phpmon.conf.json") && !Filesystem.fileExists("~/.config/phpmon/config.json") {
Log.info("An outdated configuration file was found. Moving it...")
Shell.run("cp ~/.phpmon.conf.json ~/.config/phpmon/config.json")
Log.info("The configuration file was copied successfully!")
}
}
private func loadCustomPreferencesFile(_ url: URL) {
do {
customPreferences = try JSONDecoder().decode(
CustomPrefs.self,
from: try! String(contentsOf: url, encoding: .utf8).data(using: .utf8)!
)
Log.info("The ~/.config/phpmon/config.json file was successfully parsed.")
if customPreferences.hasPresets() {
Log.info("There are \(customPreferences.presets!.count) custom presets.")
}
if customPreferences.hasServices() {
Log.info("There are custom services: \(customPreferences.services!)")
}
} catch {
Log.warn("The ~/.config/phpmon/config.json file seems to be missing or malformed.")
}
}
} }

View File

@@ -0,0 +1,38 @@
//
// PreferencesWindowController+Hotkey.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 25/07/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Cocoa
extension PreferencesWindowController {
// MARK: - Key Interaction
override func keyDown(with event: NSEvent) {
super.keyDown(with: event)
guard let tabVC = self.contentViewController as? NSTabViewController else {
return
}
guard let vc = tabVC.tabViewItems[tabVC.selectedTabViewItemIndex].viewController as? GenericPreferenceVC else {
return
}
if vc.listeningForHotkeyView == nil {
return
}
if event.keyCode == Keys.Escape || event.keyCode == Keys.Space {
Log.info("A blacklisted key was pressed, canceling listen!")
vc.listeningForHotkeyView!.unregister(nil)
} else {
vc.listeningForHotkeyView!.updateShortcut(event)
}
}
}

View File

@@ -1,5 +1,5 @@
// //
// PrefsWC.swift // PreferencesWindowController.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 02/04/2021. // Created by Nico Verbruggen on 02/04/2021.
@@ -8,12 +8,7 @@
import Cocoa import Cocoa
struct Keys { class PreferencesWindowController: PMWindowController {
static let Escape = 53
static let Space = 49
}
class PrefsWC: PMWindowController {
// MARK: - Window Identifier // MARK: - Window Identifier
@@ -26,13 +21,14 @@ class PrefsWC: PMWindowController {
let windowController = storyboard.instantiateController( let windowController = storyboard.instantiateController(
withIdentifier: "preferencesWindow" withIdentifier: "preferencesWindow"
) as! PrefsWC ) as! PreferencesWindowController
windowController.window!.title = "prefs.title".localized guard let window = windowController.window else { return }
windowController.window!.subtitle = "prefs.subtitle".localized
windowController.window!.delegate = delegate window.title = "prefs.title".localized
windowController.window!.styleMask = [.titled, .closable, .miniaturizable] window.subtitle = "prefs.subtitle".localized
windowController.window!.delegate = windowController window.delegate = delegate ?? windowController
window.styleMask = [.titled, .closable, .miniaturizable]
App.shared.preferencesWindowController = windowController App.shared.preferencesWindowController = windowController
} }
@@ -54,7 +50,7 @@ class PrefsWC: PMWindowController {
for vc in preferencesWC.tabVCs { for vc in preferencesWC.tabVCs {
tabVC.addChild(vc.viewController) tabVC.addChild(vc.viewController)
let item = tabVC.tabViewItem(for: vc.viewController) let item = tabVC.tabViewItem(for: vc.viewController)
item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "") item?.image = NSImage(systemSymbolName: vc.icon, accessibilityDescription: "\(vc.label) Icon")
item?.label = vc.label item?.label = vc.label
} }
@@ -75,6 +71,8 @@ class PrefsWC: PMWindowController {
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
// MARK: - Tabs
struct PrefTabView { struct PrefTabView {
let viewController: GenericPreferenceVC let viewController: GenericPreferenceVC
let label: String let label: String
@@ -93,6 +91,11 @@ class PrefsWC: PMWindowController {
label: "Appearance", label: "Appearance",
icon: "paintbrush" icon: "paintbrush"
), ),
PrefTabView(
viewController: MenuStructurePreferencesVC.fromStoryboard(),
label: "Visibility",
icon: "eye"
),
PrefTabView( PrefTabView(
viewController: NotificationPreferencesVC.fromStoryboard(), viewController: NotificationPreferencesVC.fromStoryboard(),
label: "Notifications", label: "Notifications",
@@ -101,29 +104,4 @@ class PrefsWC: PMWindowController {
] ]
}() }()
// MARK: - Key Interaction
override func keyDown(with event: NSEvent) {
super.keyDown(with: event)
guard let tabVC = self.contentViewController as? NSTabViewController else {
return
}
guard let selected = tabVC.tabViewItems[tabVC.selectedTabViewItemIndex].viewController else {
return
}
if let vc = selected as? GenericPreferenceVC {
if vc.listeningForHotkeyView != nil {
if event.keyCode == Keys.Escape || event.keyCode == Keys.Space {
Log.info("A blacklisted key was pressed, canceling listen!")
vc.listeningForHotkeyView!.unregister(nil)
} else {
vc.listeningForHotkeyView!.updateShortcut(event)
}
}
}
}
} }

View File

@@ -114,6 +114,20 @@ class GenericPreferenceVC: NSViewController {
) )
} }
func getShowPhpDoctorSuggestionsPV() -> NSView {
return CheckboxPreferenceView.make(
sectionText: "prefs.php_doctor".localized,
descriptionText: "prefs.php_doctor_suggestions_desc".localized,
checkboxText: "prefs.php_doctor_suggestions_title".localized,
preference: .showPhpDoctorSuggestions,
action: {
MainMenu.shared.refreshIcon()
MainMenu.shared.rebuild()
}
)
}
func getNotifyAboutVersionChangePV() -> NSView { func getNotifyAboutVersionChangePV() -> NSView {
return CheckboxPreferenceView.make( return CheckboxPreferenceView.make(
sectionText: "prefs.notifications".localized, sectionText: "prefs.notifications".localized,
@@ -174,6 +188,23 @@ class GenericPreferenceVC: NSViewController {
) )
} }
func getDisplayMenuSectionPV(
_ localizationKey: String,
_ preference: PreferenceName,
_ first: Bool = false
) -> NSView {
return CheckboxPreferenceView.make(
sectionText: first ? "prefs.menu_contents".localized : "",
descriptionText: "\(localizationKey)_desc".localized,
checkboxText: localizationKey.localized,
preference: preference,
action: {
MainMenu.shared.refreshIcon()
MainMenu.shared.rebuild()
}
)
}
// MARK: - Listening for hotkey delegate // MARK: - Listening for hotkey delegate
var listeningForHotkeyView: HotkeyPreferenceView? var listeningForHotkeyView: HotkeyPreferenceView?
@@ -194,6 +225,7 @@ class GeneralPreferencesVC: GenericPreferenceVC {
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC .instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
vc.views = [ vc.views = [
vc.getShowPhpDoctorSuggestionsPV(),
vc.getAutoRestartPV(), vc.getAutoRestartPV(),
vc.getAutomaticComposerUpdatePV(), vc.getAutomaticComposerUpdatePV(),
vc.getShortcutPV(), vc.getShortcutPV(),
@@ -225,9 +257,30 @@ class NotificationPreferencesVC: GenericPreferenceVC {
} }
class AppearancePreferencesVC: GenericPreferenceVC { class MenuStructurePreferencesVC: GenericPreferenceVC {
// MARK: - Lifecycle public static func fromStoryboard() -> GenericPreferenceVC {
let vc = NSStoryboard(name: "Main", bundle: nil)
.instantiateController(withIdentifier: "preferencesTemplateVC") as! GenericPreferenceVC
vc.views = [
vc.getDisplayMenuSectionPV("prefs.display_global_version_switcher", .displayGlobalVersionSwitcher, true),
vc.getDisplayMenuSectionPV("prefs.display_services_manager", .displayServicesManager),
vc.getDisplayMenuSectionPV("prefs.display_valet_integration", .displayValetIntegration),
vc.getDisplayMenuSectionPV("prefs.display_php_config_finder", .displayPhpConfigFinder),
vc.getDisplayMenuSectionPV("prefs.display_composer_toolkit", .displayComposerToolkit),
vc.getDisplayMenuSectionPV("prefs.display_limits_widget", .displayLimitsWidget),
vc.getDisplayMenuSectionPV("prefs.display_extensions", .displayExtensions),
vc.getDisplayMenuSectionPV("prefs.display_presets", .displayPresets),
vc.getDisplayMenuSectionPV("prefs.display_misc", .displayMisc)
]
return vc
}
}
class AppearancePreferencesVC: GenericPreferenceVC {
public static func fromStoryboard() -> GenericPreferenceVC { public static func fromStoryboard() -> GenericPreferenceVC {
let vc = NSStoryboard(name: "Main", bundle: nil) let vc = NSStoryboard(name: "Main", bundle: nil)

View File

@@ -107,13 +107,13 @@ struct Preset: Codable, Equatable {
// Show the correct notification // Show the correct notification
if wasRollback { if wasRollback {
LocalNotification.send( await LocalNotification.send(
title: "notification.preset_reverted_title".localized, title: "notification.preset_reverted_title".localized,
subtitle: "notification.preset_reverted_desc".localized, subtitle: "notification.preset_reverted_desc".localized,
preference: .notifyAboutPresets preference: .notifyAboutPresets
) )
} else { } else {
LocalNotification.send( await LocalNotification.send(
title: "notification.preset_applied_title".localized, title: "notification.preset_applied_title".localized,
subtitle: "notification.preset_applied_desc".localized(self.name), subtitle: "notification.preset_applied_desc".localized(self.name),
preference: .notifyAboutPresets preference: .notifyAboutPresets
@@ -167,14 +167,15 @@ struct Preset: Codable, Equatable {
// MARK: - Menu Items // MARK: - Menu Items
// swiftlint:disable void_function_in_ternary
public func getMenuItemText() -> String { public func getMenuItemText() -> String {
var info = extensions.count == 1 var info = extensions.count == 1
? "preset.extension".localized(extensions.count) ? "preset.extension".localized(extensions.count)
: "preset.extensions".localized(extensions.count) : "preset.extensions".localized(extensions.count)
info += ", " info += ", "
info += configuration.count == 1 info += configuration.count == 1
? "preset.preference".localized(configuration.count) ? "preset.preference".localized(configuration.count)
: "preset.preferences".localized(configuration.count) : "preset.preferences".localized(configuration.count)
if self.version == nil { if self.version == nil {
return "<span style=\"font-family: '-apple-system'; font-size: 12px;\">" return "<span style=\"font-family: '-apple-system'; font-size: 12px;\">"
@@ -263,7 +264,7 @@ struct Preset: Codable, Equatable {
try! String(data: data, encoding: .utf8)! try! String(data: data, encoding: .utf8)!
.write( .write(
toFile: "/Users/\(Paths.whoami)/.config/phpmon/preset_revert.json", toFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
atomically: true, atomically: true,
encoding: .utf8 encoding: .utf8
) )

View File

@@ -16,7 +16,7 @@ class PresetHelper {
public static func loadRollbackPresetFromFile() { public static func loadRollbackPresetFromFile() {
guard let revert = try? String( guard let revert = try? String(
contentsOfFile: "/Users/\(Paths.whoami)/.config/phpmon/preset_revert.json", contentsOfFile: "\(Paths.homePath)/.config/phpmon/preset_revert.json",
encoding: .utf8 encoding: .utf8
) else { ) else {
PresetHelper.rollbackPreset = nil PresetHelper.rollbackPreset = nil

View File

@@ -0,0 +1,24 @@
//
// ProgressVC.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 26/07/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import AppKit
class ProgressViewController: NSViewController {
@IBOutlet weak var labelTitle: NSTextField!
@IBOutlet weak var labelDescription: NSTextField!
@IBOutlet var textView: NSTextView!
@IBOutlet weak var imageViewType: NSImageView!
deinit {
Log.perf("Deinitializing ProgressViewController")
}
}

View File

@@ -1,15 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Window Controller--> <!--Window Controller-->
<scene sceneID="pUZ-6w-gUX"> <scene sceneID="pUZ-6w-gUX">
<objects> <objects>
<windowController storyboardIdentifier="progressWindow" id="LSr-Iw-X1T" customClass="ProgressWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController"> <windowController storyboardIdentifier="progressWindow" id="LSr-Iw-X1T" customClass="TerminalProgressWindowController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="none" frameAutosaveName="" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="PD9-0p-i0S" customClass="NSPanel"> <window key="window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="none" frameAutosaveName="" titlebarAppearsTransparent="YES" titleVisibility="hidden" id="PD9-0p-i0S" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" rightStrut="YES" topStrut="YES"/> <windowPositionMask key="initialPositionMask" rightStrut="YES" topStrut="YES"/>
@@ -43,7 +42,7 @@
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/> <rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
<clipView key="contentView" drawsBackground="NO" id="2Mc-oy-AzN"> <clipView key="contentView" drawsBackground="NO" id="2Mc-oy-AzN">
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/> <rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="d1T-N1-CRe"> <textView importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="d1T-N1-CRe">
<rect key="frame" x="0.0" y="0.0" width="591" height="210"/> <rect key="frame" x="0.0" y="0.0" width="591" height="210"/>
@@ -65,11 +64,11 @@
</textView> </textView>
</subviews> </subviews>
</clipView> </clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="51v-CN-AuA"> <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="51v-CN-AuA">
<rect key="frame" x="-100" y="-100" width="225" height="15"/> <rect key="frame" x="-100" y="-100" width="225" height="15"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="lSO-JG-QOf"> <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="lSO-JG-QOf">
<rect key="frame" x="-100" y="-100" width="15" height="173"/> <rect key="frame" x="-100" y="-100" width="15" height="173"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>

View File

@@ -1,5 +1,5 @@
// //
// ProgressView.swift // TerminalProgressWindowController.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 18/12/2021. // Created by Nico Verbruggen on 18/12/2021.
@@ -9,14 +9,14 @@
import Foundation import Foundation
import AppKit import AppKit
class ProgressWindowController: NSWindowController, NSWindowDelegate { class TerminalProgressWindowController: NSWindowController, NSWindowDelegate {
static func display(title: String, description: String) -> ProgressWindowController { static func display(title: String, description: String) -> TerminalProgressWindowController {
let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil) let storyboard = NSStoryboard(name: "ProgressWindow", bundle: nil)
let windowController = storyboard.instantiateController( let windowController = storyboard.instantiateController(
withIdentifier: "progressWindow" withIdentifier: "progressWindow"
) as! ProgressWindowController ) as! TerminalProgressWindowController
windowController.showWindow(windowController) windowController.showWindow(windowController)
windowController.window?.makeKeyAndOrderFront(nil) windowController.window?.makeKeyAndOrderFront(nil)
@@ -60,17 +60,3 @@ class ProgressWindowController: NSWindowController, NSWindowDelegate {
} }
} }
class ProgressViewController: NSViewController {
@IBOutlet weak var labelTitle: NSTextField!
@IBOutlet weak var labelDescription: NSTextField!
@IBOutlet var textView: NSTextView!
@IBOutlet weak var imageViewType: NSImageView!
deinit {
Log.perf("Deinitializing ProgressViewController")
}
}

View File

@@ -10,8 +10,13 @@ import Foundation
import SwiftUI import SwiftUI
var isRunningSwiftUIPreview: Bool { var isRunningSwiftUIPreview: Bool {
return ProcessInfo.processInfo #if DEBUG
.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil // If running SwiftUI *and* when debugging
return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] != nil
#else
// Release builds should always return false here
return false
#endif
} }
extension Color { extension Color {

View File

@@ -0,0 +1,37 @@
//
// NoDomainResults.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 15/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct NoDomainResults: View {
@State var searching: Bool = false
var body: some View {
VStack(alignment: .center, spacing: 15) {
Image(systemName: searching ? "magnifyingglass.circle.fill" : "questionmark.circle.fill")
.resizable()
.renderingMode(.template)
.frame(width: 24, height: 24)
VStack(alignment: .center) {
Text(
searching
? "domain_list.no_domains_for_search_query".localizedForSwiftUI
: "domain_list.no_domains".localizedForSwiftUI
)
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding(25)
}
}
struct NoDomainResults_Previews: PreviewProvider {
static var previews: some View {
NoDomainResults()
}
}

View File

@@ -26,11 +26,17 @@ struct HeaderView: View {
static func asMenuItem( static func asMenuItem(
text: String, text: String,
width: Int? = nil minimumWidth: CGFloat? = nil
) -> NSMenuItem { ) -> NSMenuItem {
let view = NSHostingView(rootView: Self(text: text)) let view = NSHostingView(rootView: Self(text: text))
view.autoresizingMask = [.width, .height] view.autoresizingMask = [.width, .height]
view.setFrameSize(CGSize(width: view.frame.width, height: 24))
var finalWidth = view.frame.width
if minimumWidth != nil && minimumWidth! > finalWidth {
finalWidth = minimumWidth!
}
view.setFrameSize(CGSize(width: finalWidth, height: 24))
let item = NSMenuItem() let item = NSMenuItem()
item.view = view item.view = view

View File

@@ -56,7 +56,9 @@ struct ServicesView: View {
}.frame(minWidth: 0, maxWidth: .infinity) }.frame(minWidth: 0, maxWidth: .infinity)
} else { } else {
// Empty cell // Empty cell
VStack { EmptyView() }.frame(minWidth: 0, maxWidth: .infinity) VStack {
EmptyView()
}.frame(minWidth: 0, maxWidth: .infinity)
} }
} }
} }

View File

@@ -0,0 +1,127 @@
//
// OnboardingView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 08/07/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct OnboardingTextItem: View {
@State var icon: String
@State var title: String
@State var description: String
var body: some View {
HStack(alignment: .top, spacing: 5) {
Image(systemName: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 24, height: 24)
.foregroundColor(Color.appPrimary)
.padding(.trailing, 10)
VStack(alignment: .leading, spacing: 4) {
Text(title.localizedForSwiftUI)
.font(.system(size: 14))
.lineLimit(3)
Text(description.localizedForSwiftUI)
.foregroundColor(Color.secondary)
.font(.system(size: 13))
.lineLimit(4)
.fixedSize(horizontal: false, vertical: true)
.frame(minWidth: 0, maxWidth: 800, alignment: .leading)
}
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray.opacity(0.3), lineWidth: 1))
}
}
struct OnboardingView: View {
var body: some View {
VStack(alignment: .center, spacing: 5) {
HStack {
Image(nsImage: NSApp.applicationIconImage)
.resizable()
.frame(width: 80, height: 80)
.padding(.bottom, 5)
.padding(.trailing, 25)
VStack(alignment: .leading, spacing: 0) {
Text("onboarding.welcome".localized)
.font(.title)
.bold()
.padding(.bottom, 5)
Text("onboarding.explore".localized)
.padding(.bottom)
.padding(.trailing)
}
.padding(.top, 10)
}
.padding(.leading)
.padding(.trailing)
VStack {
VStack(alignment: .leading, spacing: 10) {
OnboardingTextItem(
icon: "bolt.circle.fill",
title: "onboarding.tour.menu_bar.title",
description: "onboarding.tour.menu_bar"
)
OnboardingTextItem(
icon: "checkmark.circle.fill",
title: "onboarding.tour.services.title",
description: "onboarding.tour.services"
)
OnboardingTextItem(
icon: "list.bullet.circle.fill",
title: "onboarding.tour.domains.title",
description: "onboarding.tour.domains"
)
OnboardingTextItem(
icon: "pin.circle.fill",
title: "onboarding.tour.isolation.title",
description: "onboarding.tour.isolation"
)
}
}.padding()
VStack(spacing: 20) {
HStack {
Image(systemName: "questionmark.circle.fill")
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(Color.appSecondary)
.padding(.trailing, 10)
HStack {
Text("onboarding.tour.faq_hint".localizedForSwiftUI)
.lineLimit(5)
}.fixedSize(horizontal: false, vertical: true)
}
VStack {
Text("onboarding.tour.once".localized)
.font(.subheadline)
.foregroundColor(.gray)
.padding(.top, 5)
.padding(.bottom, 5)
.lineLimit(5)
Button("onboarding.tour.close".localized) {
App.shared.onboardingWindowController?.close()
}
}
}
.padding(.leading)
.padding(.trailing)
}
}
}
struct OnboardingView_Previews: PreviewProvider {
static var previews: some View {
Group {
OnboardingView()
OnboardingView().preferredColorScheme(.dark)
}
}
}

View File

@@ -0,0 +1,32 @@
//
// NoWarningsView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 15/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct NoWarningsView: View {
var body: some View {
VStack(alignment: .center, spacing: 15) {
Image(systemName: "checkmark.circle.fill")
.resizable()
.renderingMode(.template)
.foregroundColor(Color.green)
.frame(width: 24, height: 24)
VStack(alignment: .center) {
Text("warnings.none".localizedForSwiftUI)
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.padding(25)
}
}
struct NoWarningsView_Previews: PreviewProvider {
static var previews: some View {
NoWarningsView()
}
}

View File

@@ -0,0 +1,91 @@
//
// WarningListView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct WarningListView: View {
@State var warnings: [Warning]
init(empty: Bool = false) {
self.warnings = empty ? [] : WarningManager.shared.warnings
}
var body: some View {
VStack {
HStack(alignment: .center, spacing: 15) {
Image(systemName: "stethoscope.circle.fill")
.resizable()
.frame(width: 40, height: 40)
.foregroundColor(Color.red)
.padding(12)
VStack(alignment: .leading, spacing: 5) {
Text("warnings.description".localizedForSwiftUI)
.font(.system(size: 12))
.frame(maxWidth: .infinity, alignment: .leading)
Text("warnings.disclaimer".localizedForSwiftUI)
.font(.system(size: 12))
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.padding(10)
Divider()
HStack(alignment: .center, spacing: 15) {
Button("warnings.refresh.button".localizedForSwiftUI) {
Task {
await WarningManager.shared.checkEnvironment()
self.warnings = WarningManager.shared.warnings
}
}
Text("warnings.refresh.button.description".localizedForSwiftUI)
.foregroundColor(.gray)
.font(.system(size: 11))
}
.padding(10)
List {
VStack(alignment: .leading, spacing: 0) {
if warnings.isEmpty {
NoWarningsView()
} else {
ForEach(warnings) { warning in
Group {
WarningView(
title: warning.title,
paragraphs: warning.paragraphs,
documentationUrl: warning.url
)
.fixedSize(horizontal: false, vertical: true)
Divider()
}.padding(5)
}
}
}.frame(minHeight: 0, maxHeight: .infinity).padding(5)
}
.listRowInsets(EdgeInsets())
.listStyle(.plain)
.frame(maxHeight: .infinity, alignment: .top)
}
}
}
struct WarningListView_Previews: PreviewProvider {
static var previews: some View {
WarningListView(empty: true)
.frame(width: 600, height: 480)
/*
WarningListView()
// TODO: Figure out how the empty() only applies to this single instance
// .empty()
.frame(width: 600, height: 480)
*/
}
}

View File

@@ -0,0 +1,69 @@
//
// WarningView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 31/07/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct WarningView: View {
@State var title: String
@State var paragraphs: [String]
@State var documentationUrl: String?
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .top, spacing: 10) {
Image(systemName: "bandage.fill")
.resizable()
.frame(width: 18, height: 18)
.foregroundColor(Color.orange)
.padding(.trailing, 5)
VStack(alignment: .leading, spacing: 15) {
VStack(alignment: .leading, spacing: 10) {
Text(title.localizedForSwiftUI)
.fontWeight(.bold)
ForEach(paragraphs, id: \.self) { paragraph in
Text(paragraph.localizedForSwiftUI)
.font(.system(size: 13))
}
}
.fixedSize(horizontal: false, vertical: false)
.frame(
minWidth: 0, maxWidth: .infinity,
minHeight: 0, maxHeight: .infinity,
alignment: .topLeading
)
if documentationUrl != nil {
Button("Learn More") {
NSWorkspace.shared.open(URL(string: documentationUrl!)!)
}
}
}
}.padding(5)
}
}
}
struct WarningView_Previews: PreviewProvider {
static var previews: some View {
WarningView(
title: "warnings.helper_permissions.title",
paragraphs: ["warnings.helper_permissions.description"],
documentationUrl: "https://nicoverbruggen.be"
)
.frame(width: 600, height: 105)
WarningView(
title: "warnings.helper_permissions.title",
paragraphs: ["warnings.helper_permissions.description"],
documentationUrl: "https://nicoverbruggen.be"
)
.preferredColorScheme(.dark)
.frame(width: 600, height: 105)
// WarningListView().frame(width: 600, height: 580)
}
}

View File

@@ -0,0 +1,36 @@
//
// Warning.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
struct Warning: Identifiable {
var id = UUID()
let command: () async -> Bool
let name: String
let title: String
let paragraphs: [String]
let url: String?
init(
command: @escaping () async -> Bool,
name: String,
title: String,
paragraphs: [String],
url: String?
) {
self.command = command
self.name = name
self.title = title
self.paragraphs = paragraphs
self.url = url
}
public func applies() async -> Bool {
return await self.command()
}
}

View File

@@ -0,0 +1,83 @@
//
// WarningManager.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import Cocoa
class WarningManager {
static var shared = WarningManager()
init() {
if isRunningSwiftUIPreview {
self.warnings = self.evaluations
}
}
public let evaluations: [Warning] = [
Warning(
command: {
return Shell.pipe("sysctl -n sysctl.proc_translated")
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
},
name: "Running PHP Monitor with Rosetta on M1",
title: "warnings.arm_compatibility.title",
paragraphs: ["warnings.arm_compatibility.description"],
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon"
),
Warning(
command: {
return !Shell.user.PATH.contains("\(Paths.homePath)/.config/phpmon/bin") &&
!FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
},
name: "Helpers cannot be symlinked and not in PATH",
title: "warnings.helper_permissions.title",
paragraphs: [
"warnings.helper_permissions.description",
"warnings.helper_permissions.unavailable",
"warnings.helper_permissions.symlink"
],
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
)
]
@Published public var warnings: [Warning] = []
public func hasWarnings() -> Bool {
return !warnings.isEmpty
}
func evaluateWarnings() {
Task { await WarningManager.shared.checkEnvironment() }
}
/**
Checks the user's environment and checks if any special warnings apply.
*/
func checkEnvironment() async {
self.warnings = []
if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil {
// For debugging purposes, we may wish to see all possible evaluations listed
self.warnings = self.evaluations
} else {
// Otherwise, loop over the actual evaluations and list the warnings
await loopOverEvaluations()
}
MainMenu.shared.rebuild()
}
private func loopOverEvaluations() async {
for check in self.evaluations where await check.applies() {
Log.info("[DOCTOR] \(check.name) (!)")
self.warnings.append(check)
continue
}
}
}

View File

@@ -0,0 +1,45 @@
//
// WarningsWindowController.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 09/08/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Cocoa
import SwiftUI
class WarningsWindowController: PMWindowController {
// MARK: - Window Identifier
override var windowName: String {
return "Warnings"
}
public static func create(delegate: NSWindowDelegate?) {
let windowController = Self()
windowController.window = NSWindow()
guard let window = windowController.window else { return }
window.title = ""
window.styleMask = [.titled, .closable, .miniaturizable]
window.titlebarAppearsTransparent = true
window.delegate = delegate ?? windowController
window.contentView = NSHostingView(rootView: WarningListView())
window.setContentSize(NSSize(width: 600, height: 480))
App.shared.warningsWindowController = windowController
}
public static func show(delegate: NSWindowDelegate? = nil) {
if App.shared.warningsWindowController == nil {
Self.create(delegate: delegate)
}
App.shared.warningsWindowController?.showWindow(self)
App.shared.warningsWindowController?.window?.setCenterPosition(offsetY: 70)
NSApp.activate(ignoringOtherApps: true)
}
}

View File

@@ -51,7 +51,7 @@ class PhpConfigWatcher {
eventMask: DispatchSource.FileSystemEvent, eventMask: DispatchSource.FileSystemEvent,
behaviour: FSWatcherBehaviour = .reloadsMenu behaviour: FSWatcherBehaviour = .reloadsMenu
) { ) {
if !Filesystem.fileExists(url.path) { if !Filesystem.exists(url.path) {
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.") Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
return return
} }

View File

@@ -55,6 +55,11 @@
"mi_detected_extensions" = "Detected Extensions"; "mi_detected_extensions" = "Detected Extensions";
"mi_no_extensions_detected" = "No additional extensions detected."; "mi_no_extensions_detected" = "No additional extensions detected.";
"mi_php_doctor" = "PHP Doctor";
"mi_fa_php_doctor" = "Open PHP Doctor...";
"mi_recommendations_count" = "%i Issue(s) Detected!";
"mi_view_recommendations" = "View Recommendations...";
"mi_valet" = "Laravel Valet"; "mi_valet" = "Laravel Valet";
"mi_domain_list" = "View Domains List..."; "mi_domain_list" = "View Domains List...";
@@ -72,6 +77,8 @@
"mi_no_presets" = "No presets available."; "mi_no_presets" = "No presets available.";
"mi_set_up_presets" = "Learn more about presets..."; "mi_set_up_presets" = "Learn more about presets...";
"mi_view_onboarding" = "Open Welcome Tour...";
"mi_xdebug_available_modes" = "Available Modes"; "mi_xdebug_available_modes" = "Available Modes";
"mi_xdebug_actions" = "Actions"; "mi_xdebug_actions" = "Actions";
"mi_xdebug_disable_all" = "Disable All Modes"; "mi_xdebug_disable_all" = "Disable All Modes";
@@ -93,6 +100,9 @@
"domain_list.title" = "Domains"; "domain_list.title" = "Domains";
"domain_list.subtitle" = ""; "domain_list.subtitle" = "";
"domain_list.no_domains" = "You have not set up any domains or proxies yet.";
"domain_list.no_domains_for_search_query" = "There are no results for your search query.";
"domain_list.tooltips.isolated" = "This domain is isolated and using PHP %@ instead of the globally linked PHP."; "domain_list.tooltips.isolated" = "This domain is isolated and using PHP %@ instead of the globally linked PHP.";
"domain_list.tooltips.checkmark" = "This domain is being served with a version of PHP that is compatible with this requirement (PHP %@). Click on the PHP version next to this checkmark to find out more information about how this requirement was determined."; "domain_list.tooltips.checkmark" = "This domain is being served with a version of PHP that is compatible with this requirement (PHP %@). Click on the PHP version next to this checkmark to find out more information about how this requirement was determined.";
@@ -157,10 +167,13 @@
// SITE LIST ACTIONS // SITE LIST ACTIONS
"domain_list.isolate" = "Isolate Domain"; "domain_list.isolate" = "Switch PHP Version";
"domain_list.site_isolation" = "Site Isolation";
"domain_list.remove_isolation" = "Remove Isolation"; "domain_list.remove_isolation" = "Remove Isolation";
"domain_list.always_use_php" = "Always use PHP %@";
"domain_list.isolation_unavailable" = "Isolation Not Supported (in Valet 2)"; "domain_list.isolation_unavailable" = "Isolation Not Supported (in Valet 2)";
"domain_list.actions" = "Actions";
"domain_list.unlink" = "Unlink Directory"; "domain_list.unlink" = "Unlink Directory";
"domain_list.secure" = "Secure Domain"; "domain_list.secure" = "Secure Domain";
"domain_list.unsecure" = "Unsecure Domain"; "domain_list.unsecure" = "Unsecure Domain";
@@ -210,9 +223,11 @@
"prefs.info_density" = "Info Density:"; "prefs.info_density" = "Info Density:";
"prefs.services" = "Services:"; "prefs.services" = "Services:";
"prefs.switcher" = "Switcher:"; "prefs.switcher" = "Switcher:";
"prefs.php_doctor" = "PHP Doctor:";
"prefs.integrations" = "Integrations:"; "prefs.integrations" = "Integrations:";
"prefs.updates" = "Updates:"; "prefs.updates" = "Updates:";
"prefs.notifications" = "Notifications:"; "prefs.notifications" = "Notifications:";
"prefs.menu_contents" = "Features in Menu:";
"prefs.icon_options.php" = "Display PHP Icon"; "prefs.icon_options.php" = "Display PHP Icon";
"prefs.icon_options.elephant" = "Display Elephant Icon"; "prefs.icon_options.elephant" = "Display Elephant Icon";
@@ -238,6 +253,9 @@
"prefs.automatic_update_check_title" = "Automatically check for updates"; "prefs.automatic_update_check_title" = "Automatically check for updates";
"prefs.automatic_update_check_desc" = "When checked, PHP Monitor will automatically check if there is a newer version available, and notify you if that is the case."; "prefs.automatic_update_check_desc" = "When checked, PHP Monitor will automatically check if there is a newer version available, and notify you if that is the case.";
"prefs.php_doctor_suggestions_title" = "Always show suggestions";
"prefs.php_doctor_suggestions_desc" = "If you uncheck this item, no PHP Doctor suggestions will appear in PHP Monitor's menu. Keep in mind that PHP Doctor will not appear if there are no recommendations.";
"prefs.shortcut_set" = "Set global shortcut"; "prefs.shortcut_set" = "Set global shortcut";
"prefs.shortcut_listening" = "<listening for keypress>"; "prefs.shortcut_listening" = "<listening for keypress>";
"prefs.shortcut_clear" = "Clear"; "prefs.shortcut_clear" = "Clear";
@@ -261,6 +279,33 @@
"prefs.notify_about_composer_success_desc" = "Displays a notification when the global Composer configuration was successfully updated."; "prefs.notify_about_composer_success_desc" = "Displays a notification when the global Composer configuration was successfully updated.";
"prefs.notify_about_composer_success" = "Notify about global composer update"; "prefs.notify_about_composer_success" = "Notify about global composer update";
"prefs.display_global_version_switcher_desc" = "If disabled, you will not be able to change the globally linked PHP version via the main menu.";
"prefs.display_global_version_switcher" = "PHP Switcher";
"prefs.display_services_manager_desc" = "If disabled, you will not be able to see, start or stop individual services. (If any services are disabled, you will not be easily able to see that this is the case.)";
"prefs.display_services_manager" = "Services Manager";
"prefs.display_valet_integration_desc" = "If disabled, you will not be able to locate the main Valet folder or open the list of domains.";
"prefs.display_valet_integration" = "Valet Integration";
"prefs.display_php_config_finder_desc" = "If disabled, you will not be able to easily locate your PHP configuration files and/or generate a phpinfo() dump.";
"prefs.display_php_config_finder" = "PHP Config Finder";
"prefs.display_composer_toolkit_desc" = "If disabled, you will not be able to invoke Composer via the main menu. The automatic Composer update after switching preference is unaffected by this change.";
"prefs.display_composer_toolkit" = "Composer Toolkit";
"prefs.display_limits_widget_desc" = "If disabled, you will not be able to see the limits widget (memory, POST, upload) in the main menu.";
"prefs.display_limits_widget" = "Limits Widget";
"prefs.display_extensions_desc" = "If disabled, you will not be able to easily toggle extensions via the main menu.";
"prefs.display_extensions" = "Extensions";
"prefs.display_presets_desc" = "If disabled, you will not be able to apply or revert PHP configuration presets.";
"prefs.display_presets" = "Presets";
"prefs.display_misc_desc" = "If disabled, you will not be able to access the First Aid & Services menu.";
"prefs.display_misc" = "First Aid & Services Menu";
// NOTIFICATIONS // NOTIFICATIONS
"notification.version_changed_title" = "PHP %@ now active"; "notification.version_changed_title" = "PHP %@ now active";
@@ -354,7 +399,7 @@ problem manually, using your own Terminal app (this just shows you the output)."
// PHP FPM Broken // PHP FPM Broken
"alert.php_fpm_broken.title" = "Your PHP-FPM configuration is not pointing at the Valet socket!"; "alert.php_fpm_broken.title" = "Your PHP-FPM configuration is not pointing at the Valet socket!";
"alert.php_fpm_broken.info" = "PHP Monitor has determined that there are issues with your PHP-FPM config. This will result in '502 Bad Gateway' responses if you visit websites linked via Valet."; "alert.php_fpm_broken.info" = "PHP Monitor has determined that there are issues with your PHP-FPM config. This will result in '502 Bad Gateway' responses if you visit websites linked via Valet.";
"alert.php_fpm_broken.description" = "If it's been a while, you can usually fix this by running `valet install`, which updates your PHP-FPM configuration.\n\nIf you are seeing this message and you are trying to run a pre-release version of PHP, it is possible that Valet does not support this pre-release version of PHP yet.\n\nIf that is the case, you can try the following workaround: edit the file at `~/.composer/vendor/laravel/valet/cli/Valet/Brew.php` and add e.g. `php@8.2` to the `SUPPORTED_PHP_VERSIONS` array. After editing the file, try running `valet install`. (This will, if all goes well, set up all the required Valet configuration files.)"; "alert.php_fpm_broken.description" = "If it's been a while, you can usually fix this by running `valet install`, which updates your PHP-FPM configuration.\n\nIf you are seeing this message and you are trying to run a pre-release version of PHP, it is possible that Valet does not support this pre-release version of PHP yet.\n\nYou may need to upgrade your installation of Laravel Valet to at least v3.1.11, after that you should run `valet install`. More information here: https://phpmon.app/prerelease-php";
// PHP Monitor Cannot Start // PHP Monitor Cannot Start
"alert.cannot_start.title" = "PHP Monitor cannot start due to a problem with your system configuration"; "alert.cannot_start.title" = "PHP Monitor cannot start due to a problem with your system configuration";
@@ -435,6 +480,12 @@ You can do this by running `composer global update` in your terminal. After that
"startup.errors.valet_version_unknown.subtitle" = "Parsing the output of `valet --version` failed. Make sure your Valet installation works and is up-to-date."; "startup.errors.valet_version_unknown.subtitle" = "Parsing the output of `valet --version` failed. Make sure your Valet installation works and is up-to-date.";
"startup.errors.valet_version_unknown.desc" = "Try running `valet --version` in a terminal to find out what's going on."; "startup.errors.valet_version_unknown.desc" = "Try running `valet --version` in a terminal to find out what's going on.";
"startup.errors.valet_not_installed.title" = "Your Valet configuration directory is missing";
"startup.errors.valet_not_installed.subtitle" = "The required directory `~/.config/valet` is missing. This usually means that you forgot to run `valet install`.";
"startup.errors.valet_not_installed.desc" = "Assuming you already installed Valet via Composer, please run `valet install` to finish setting up Laravel Valet.
If you are seeing this message but are confused why this folder has gone missing, then you may want to investigate why it is gone—it shouldn't just disappear and it means your Valet installation is broken.";
/// Brew & sudoers /// Brew & sudoers
"startup.errors.sudoers_brew.title" = "Brew has not been added to sudoers.d"; "startup.errors.sudoers_brew.title" = "Brew has not been added to sudoers.d";
"startup.errors.sudoers_brew.subtitle" = "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue."; "startup.errors.sudoers_brew.subtitle" = "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue.";
@@ -497,3 +548,38 @@ You can do this by running `composer global update` in your terminal. After that
"alert.warnings.tld_issue.title" = "You are not using `.test` as the TLD for Valet."; "alert.warnings.tld_issue.title" = "You are not using `.test` as the TLD for Valet.";
"alert.warnings.tld_issue.subtitle" = "Using a non-default TLD may not work correctly and is not officially supported."; "alert.warnings.tld_issue.subtitle" = "Using a non-default TLD may not work correctly and is not officially supported.";
"alert.warnings.tld_issue.description" = "PHP Monitor will remain functional, but there might be issues: the app might not correctly show which domains have been secured. For optimal results, go to your Valet configuration file (config.json in the Valet directory) and change the TLD back to `test`."; "alert.warnings.tld_issue.description" = "PHP Monitor will remain functional, but there might be issues: the app might not correctly show which domains have been secured. For optimal results, go to your Valet configuration file (config.json in the Valet directory) and change the TLD back to `test`.";
// WARNINGS
"warnings.title" = "PHP Doctor";
"warnings.description" = "**PHP Doctor** will suggest improvements to your active system configuration.";
"warnings.disclaimer" = "You may choose to hide all recommendations from the PHP Monitor menu in Preferences, but it is recommended that you deal with all actionable items.";
"warnings.refresh.button" = "Scan Again";
"warnings.refresh.button.description" = "Press this button once you've fixed an issue. This will cause PHP Monitor to re-evaluate your environment. If it's really fixed, the recommendation should disappear.";
"warnings.helper_permissions.title" = "PHP Monitors helpers are currently unavailable.";
"warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version.";
"warnings.helper_permissions.unavailable" = "However, these helpers are potentially *unavailable* because PHP Monitor cannot currently create or update the required symlinks.";
"warnings.helper_permissions.symlink" = "If you do not wish to make `/usr/local/bin` writable, you can add PHP Monitor's helper directory to your `PATH` variable to make this warning go away. (Click on ”Learn More” to find out how to fix this issue.)";
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
"warnings.none" = "There are no recommendations available for you right now. You're all good!";
// ONBOARDING
"onboarding.title" = "Welcome Tour";
"onboarding.welcome" = "Welcome to PHP Monitor!";
"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer. You can find a more comprehensive list of features on GitHub.";
"onboarding.tour.menu_bar.title" = "Power In Your Menu Bar";
"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From this menu, you can access most of PHP Monitor's key functionality, including switching the globally linked PHP version, locating config files, and much more.";
"onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers.";
"onboarding.tour.services.title" = "Manage Homebrew Services";
"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it.";
"onboarding.tour.domains.title" = "Manage Domains";
"onboarding.tour.domains" = "By opening the Domains window via the menu bar item, you can view which domains are linked and parked, as well as active nginx proxies.";
"onboarding.tour.isolation.title" = "Isolate Domains";
"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only.";
"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon (under First Aid & Services).";
"onboarding.tour.close" = "Close Tour";