1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2026-03-28 06:50:08 +01:00

♻️ Replace SelectionVC

This commit is contained in:
2026-02-18 18:11:29 +01:00
parent 2d30d512e4
commit 04d9f28edb
10 changed files with 148 additions and 261 deletions

View File

@@ -14,6 +14,14 @@
0317C17E2ED87CAB005479D2 /* NVAppUpdater in Frameworks */ = {isa = PBXBuildFile; productRef = 0317C17D2ED87CAB005479D2 /* NVAppUpdater */; };
0317C1812ED87CE1005479D2 /* NVAppUpdater in Frameworks */ = {isa = PBXBuildFile; productRef = 0317C1802ED87CE1005479D2 /* NVAppUpdater */; };
0317C1832ED87CEA005479D2 /* NVAppUpdater in Frameworks */ = {isa = PBXBuildFile; productRef = 0317C1822ED87CEA005479D2 /* NVAppUpdater */; };
031D747C2F46225600D4FF48 /* SimpleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D747B2F46225600D4FF48 /* SimpleButton.swift */; };
031D747D2F46225600D4FF48 /* SimpleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D747B2F46225600D4FF48 /* SimpleButton.swift */; };
031D747E2F46225600D4FF48 /* SimpleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D747B2F46225600D4FF48 /* SimpleButton.swift */; };
031D747F2F46225600D4FF48 /* SimpleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D747B2F46225600D4FF48 /* SimpleButton.swift */; };
031D74812F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */; };
031D74822F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */; };
031D74832F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */; };
031D74842F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */; };
031E2B692B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
031E2B6A2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
031E2B6B2B1525A7007C29E1 /* BrewPhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */; };
@@ -599,7 +607,6 @@
C471E85D28F9BB650021E251 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C471E85E28F9BB650021E251 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
C471E85F28F9BB650021E251 /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
C471E86028F9BB650021E251 /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
C471E86128F9BB650021E251 /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
C471E86228F9BB650021E251 /* AddProxyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */; };
C471E86328F9BB650021E251 /* PMTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A81CA328C67101008DD9D1 /* PMTableView.swift */; };
@@ -683,7 +690,6 @@
C471E8C028F9BB8F0021E251 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
C471E8C128F9BB8F0021E251 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
C471E8C228F9BB8F0021E251 /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
C471E8C328F9BB8F0021E251 /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
C471E8C428F9BB8F0021E251 /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
C471E8C528F9BB8F0021E251 /* AddProxyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */; };
C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4A81CA328C67101008DD9D1 /* PMTableView.swift */; };
@@ -999,8 +1005,6 @@
C4FD87A829AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87A929AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FD87AA29AB9ABD0002D701 /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1036,6 +1040,8 @@
/* Begin PBXFileReference section */
0309E6662B0D4B2F002AC007 /* BrewExtensionsObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewExtensionsObservable.swift; sourceTree = "<group>"; };
031D747B2F46225600D4FF48 /* SimpleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleButton.swift; sourceTree = "<group>"; };
031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectDomainTypeView.swift; sourceTree = "<group>"; };
031E2B682B1525A7007C29E1 /* BrewPhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewPhpExtension.swift; sourceTree = "<group>"; };
031F247F2EA1071700CFB8D9 /* Container+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+Fake.swift"; sourceTree = "<group>"; };
031F24842EA1132300CFB8D9 /* PHP Monitor.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PHP Monitor.xctestplan"; sourceTree = "<group>"; };
@@ -1355,7 +1361,6 @@
C4FC8D412A49816300FBBD16 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
C4FC8D432A49816C00FBBD16 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
C4FC8D442A4981BC00FBBD16 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -1967,7 +1972,6 @@
03DAD3A52EB3B08A003417BD /* DomainListVC+Certs.swift */,
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */,
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */,
C4FE011028084FC200D1DE6D /* SelectionVC.swift */,
C4930849279F331F009C240B /* AddSiteVC.swift */,
C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */,
C44067F327E256560045BD4E /* Cells */,
@@ -2261,6 +2265,7 @@
C4B609162853AA9A00C95265 /* Common */ = {
isa = PBXGroup;
children = (
031D747B2F46225600D4FF48 /* SimpleButton.swift */,
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */,
C451AFF52969E40F0078E617 /* HelpButton.swift */,
C4EA3C462BA4F947007B0BA7 /* CustomButtonStyles.swift */,
@@ -2284,6 +2289,7 @@
C4B609182853AAA700C95265 /* Domains */ = {
isa = PBXGroup;
children = (
031D74802F46225C00D4FF48 /* SelectDomainTypeView.swift */,
0392CDEA2EB25371009176DA /* SecurePopoverView.swift */,
C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */,
);
@@ -2953,6 +2959,7 @@
03DAD3A72EB3B08F003417BD /* DomainListVC+Certs.swift in Sources */,
C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */,
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
031D747C2F46225600D4FF48 /* SimpleButton.swift in Sources */,
C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */,
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
@@ -2974,7 +2981,6 @@
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
03CC1FF42E3D23130050FC18 /* ZshRunCommand.swift in Sources */,
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */,
033D459E2B0D513900070080 /* RemovePhpExtensionCommand.swift in Sources */,
C4709CA228524B3400088BB8 /* StatsView.swift in Sources */,
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
@@ -2994,6 +3000,7 @@
039E1D7C2E5F0F300072D13D /* ValetUpgrader.swift in Sources */,
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
036C390D2E5C8CC5008DAEDF /* PackagistP2Response.swift in Sources */,
031D74842F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */,
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
C485707028BF452300539B36 /* PhpDoctorWindowController.swift in Sources */,
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
@@ -3117,12 +3124,12 @@
C471E85C28F9BB650021E251 /* DomainListWindowController.swift in Sources */,
C471E85D28F9BB650021E251 /* DomainListVC.swift in Sources */,
C471E85E28F9BB650021E251 /* DomainListVC+ContextMenu.swift in Sources */,
031D74832F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */,
C4E2E86628FC2F1B003B070C /* XCPMApplication.swift in Sources */,
C471E85F28F9BB650021E251 /* DomainListVC+Actions.swift in Sources */,
C4D5576629C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */,
C47015042C46D7F00069AAE7 /* NVAlertExtension.swift in Sources */,
C4ACE9E329F84EDD00110766 /* PhpGuard.swift in Sources */,
C471E86028F9BB650021E251 /* SelectionVC.swift in Sources */,
C471E86128F9BB650021E251 /* AddSiteVC.swift in Sources */,
C471E86228F9BB650021E251 /* AddProxyVC.swift in Sources */,
C471E86328F9BB650021E251 /* PMTableView.swift in Sources */,
@@ -3208,6 +3215,7 @@
C471E80528F9BAD40021E251 /* ActivePhpInstallation.swift in Sources */,
C471E80628F9BAD40021E251 /* PhpInstallation.swift in Sources */,
C471E81828F9BAE80021E251 /* StringExtension.swift in Sources */,
031D747E2F46225600D4FF48 /* SimpleButton.swift in Sources */,
C4E6840B2AF26B830023ED25 /* BrewTapFormulae.swift in Sources */,
C471E7FA28F9BACB0021E251 /* InternalSwitcher.swift in Sources */,
C471E82628F9BB2E0021E251 /* ComposerJson.swift in Sources */,
@@ -3352,13 +3360,13 @@
C4BF56AE2949381100379603 /* FakeValetInteractor.swift in Sources */,
C471E8C228F9BB8F0021E251 /* DomainListVC+Actions.swift in Sources */,
C4821C5D2C2DEDE200357A68 /* AppMenu.swift in Sources */,
C471E8C328F9BB8F0021E251 /* SelectionVC.swift in Sources */,
C471E8C428F9BB8F0021E251 /* AddSiteVC.swift in Sources */,
C45B91562956123A00F4EC78 /* FakeServicesManager.swift in Sources */,
C471E8C528F9BB8F0021E251 /* AddProxyVC.swift in Sources */,
C471E8C628F9BB8F0021E251 /* PMTableView.swift in Sources */,
C471E8C728F9BB8F0021E251 /* Warning.swift in Sources */,
033E9DFC2F44D93000685F62 /* Startup+Alert.swift in Sources */,
031D74822F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */,
C471E8C828F9BB8F0021E251 /* WarningManager.swift in Sources */,
C46DC7A72C7B5BCA00F19D17 /* Favorites.swift in Sources */,
C471E8C928F9BB8F0021E251 /* PhpDoctorWindowController.swift in Sources */,
@@ -3396,6 +3404,7 @@
C4B79ECE29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */,
C471E8E028F9BB8F0021E251 /* Preset.swift in Sources */,
C471E8E128F9BB8F0021E251 /* PresetHelper.swift in Sources */,
031D747D2F46225600D4FF48 /* SimpleButton.swift in Sources */,
C471E8E228F9BB8F0021E251 /* WarningView.swift in Sources */,
C471E8E328F9BB8F0021E251 /* PhpDoctorView.swift in Sources */,
C471E8E428F9BB8F0021E251 /* NoWarningsView.swift in Sources */,
@@ -3520,6 +3529,7 @@
C40934A3298EEB2C00D25014 /* CaskFile.swift in Sources */,
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
C413E43528DA3EB100AE33C7 /* TestableShellTest.swift in Sources */,
031D747F2F46225600D4FF48 /* SimpleButton.swift in Sources */,
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */,
C42F26742805B4B400938AC7 /* ValetListable.swift in Sources */,
C46EBC4528DB95F0007ACC74 /* ShellProtocol.swift in Sources */,
@@ -3531,7 +3541,6 @@
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
039C291A2E8AA314007F5FAB /* TestableWebApi.swift in Sources */,
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
038A2B802EDDB24C00173ACF /* App+UUID.swift in Sources */,
03C099472EA15C8E00B76D43 /* Container+Real.swift in Sources */,
C4D3661B291173EA006BD146 /* DictionaryExtension.swift in Sources */,
@@ -3747,6 +3756,7 @@
C4D4CB3829C109CF00DB9F93 /* InternalSwitcher+Valet.swift in Sources */,
C4B79EB729CA387F00A483EE /* BrewPhpFormulaeHandler.swift in Sources */,
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
031D74812F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */,
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
C4513F992B13E338001AD760 /* PhpExtensionManagerView+Actions.swift in Sources */,
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,

View File

@@ -8,7 +8,7 @@
import Cocoa
import UserNotifications
@NSApplicationMain
@main
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
static var instance: AppDelegate {
@@ -55,6 +55,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
logger.verbosity = .performance
Log.info("Extra verbose mode is enabled by default on DEBUG builds.")
// No matter what, clear PHP Guard if it's a debug build
Stats.clearCurrentGlobalPhpVersion()
if let profile = CommandLine.arguments.first(where: { $0.matches(pattern: "--configuration:*") }) {
AppDelegate.initializeTestingProfile(profile.replacing("--configuration:", with: ""))
}
@@ -80,10 +83,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
Log.info("PHP MONITOR by Nico Verbruggen")
Log.info("Version \(App.version)")
Log.separator(as: .info)
}
// Initialize the crash reporter
CrashReporter.initialize()
// Initialize the crash reporter
CrashReporter.initialize()
}
// Set up final singletons
self.valet = Valet.shared
@@ -93,8 +96,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
static func initializeTestingProfile(_ path: String) {
Log.info("The configuration with path `\(path)` is being requested...")
// Clear for PHP Guard
Stats.clearCurrentGlobalPhpVersion()
// Load the configuration file
TestableConfiguration.loadFrom(path: path).apply()
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24412" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24412"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24506"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
@@ -12,6 +12,13 @@
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="PHP_Monitor" customModuleProvider="target">
<connections>
<outlet property="menuItemSites" destination="9gy-d3-Pos" id="nul-IL-YuR"/>
</connections>
</customObject>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
@@ -306,15 +313,6 @@
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="PHP Monitor Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
@@ -322,15 +320,8 @@
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="PHP_Monitor" customModuleProvider="target">
<connections>
<outlet property="menuItemSites" destination="9gy-d3-Pos" id="nul-IL-YuR"/>
</connections>
</customObject>
</objects>
<point key="canvasLocation" x="-360" y="-94"/>
<point key="canvasLocation" x="-412" y="-153"/>
</scene>
<!--Window Controller-->
<scene sceneID="PQa-AT-b2a">
@@ -1247,162 +1238,10 @@ Gw
</objects>
<point key="canvasLocation" x="-374" y="1530"/>
</scene>
<!--SelectionVC-->
<scene sceneID="UXm-Ci-yEB">
<objects>
<viewController storyboardIdentifier="addDomainChoice" id="gOD-Gu-zDG" customClass="SelectionVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="ysc-sm-sli">
<rect key="frame" x="0.0" y="0.0" width="540" height="181"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<visualEffectView blendingMode="behindWindow" material="toolTip" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="F37-zt-gM3">
<rect key="frame" x="0.0" y="0.0" width="540" height="181"/>
<subviews>
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FhN-AM-SkI">
<rect key="frame" x="20" y="20" width="104" height="24"/>
<buttonCell key="cell" type="push" title="[i18n] Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="LxP-t4-H2W">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="Zhu-D8-cLK"/>
</constraints>
<connections>
<action selector="pressedCancel:" target="gOD-Gu-zDG" id="wMp-sM-0A4"/>
</connections>
</button>
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pYe-Qu-qnK">
<rect key="frame" x="167" y="20" width="353" height="24"/>
<subviews>
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="L5n-Gw-J27">
<rect key="frame" x="0.0" y="0.0" width="168" height="24"/>
<buttonCell key="cell" type="push" title="[i18n] Create a Link" bezelStyle="rounded" image="IconLinked" imagePosition="leading" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8UP-Sw-TP6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent">l</string>
</buttonCell>
<connections>
<action selector="pressedCreateLink:" target="gOD-Gu-zDG" id="77M-Ip-GMi"/>
</connections>
</button>
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="01Z-IV-hv1">
<rect key="frame" x="176" y="0.0" width="177" height="24"/>
<buttonCell key="cell" type="push" title="[i18n] Create a Proxy" bezelStyle="rounded" image="IconProxy" imagePosition="leading" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bJ4-q8-1Ej">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent">p</string>
</buttonCell>
<connections>
<action selector="pressedCreateProxy:" target="gOD-Gu-zDG" id="UDf-lD-KCS"/>
</connections>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJK-Ke-IK3">
<rect key="frame" x="18" y="142" width="504" height="19"/>
<textFieldCell key="cell" selectable="YES" alignment="left" title="[i18n] What kind of domain would you like to set up?" id="agk-Nj-FLd">
<font key="font" metaFont="systemBold" size="15"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="urj-Xq-TrJ">
<rect key="frame" x="18" y="64" width="504" height="70"/>
<constraints>
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="tbl-AV-4qB"/>
</constraints>
<textFieldCell key="cell" selectable="YES" alignment="left" id="3i9-RG-Ift">
<font key="font" metaFont="smallSystem"/>
<string key="title">[i18n] Links are used to directly serve projects. If you have a Laravel, Symfony, WordPress, etc. folder with code, you'll want to create a link and choose the folder where your code lives.If you are in need of a proxy, you can proxy e.g. a container to a particular domain name. This can be useful in combination with Docker, for example.</string>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="FhN-AM-SkI" firstAttribute="leading" secondItem="F37-zt-gM3" secondAttribute="leading" constant="20" symbolic="YES" id="3dg-JM-MDr"/>
<constraint firstItem="fJK-Ke-IK3" firstAttribute="top" secondItem="F37-zt-gM3" secondAttribute="top" constant="20" symbolic="YES" id="FbX-Le-O7Q"/>
<constraint firstAttribute="trailing" secondItem="pYe-Qu-qnK" secondAttribute="trailing" constant="20" symbolic="YES" id="IJA-vN-Rbv"/>
<constraint firstItem="urj-Xq-TrJ" firstAttribute="leading" secondItem="fJK-Ke-IK3" secondAttribute="leading" id="JcY-ae-6ZH"/>
<constraint firstItem="urj-Xq-TrJ" firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" id="ZBI-pN-kOz"/>
<constraint firstItem="fJK-Ke-IK3" firstAttribute="leading" secondItem="F37-zt-gM3" secondAttribute="leading" constant="20" symbolic="YES" id="d4o-6b-Dho"/>
<constraint firstItem="urj-Xq-TrJ" firstAttribute="top" secondItem="fJK-Ke-IK3" secondAttribute="bottom" constant="8" symbolic="YES" id="hOk-eL-Eg0"/>
<constraint firstItem="FhN-AM-SkI" firstAttribute="top" secondItem="urj-Xq-TrJ" secondAttribute="bottom" constant="20" id="kCc-Vp-Gvq"/>
<constraint firstAttribute="bottom" secondItem="pYe-Qu-qnK" secondAttribute="bottom" constant="20" id="lPX-ZF-XZN"/>
<constraint firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" constant="20" symbolic="YES" id="spl-Bn-xtw"/>
<constraint firstAttribute="bottom" secondItem="FhN-AM-SkI" secondAttribute="bottom" constant="20" symbolic="YES" id="t5w-aL-tOa"/>
<constraint firstItem="pYe-Qu-qnK" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="FhN-AM-SkI" secondAttribute="trailing" constant="8" symbolic="YES" id="y7k-sl-xqe"/>
</constraints>
</visualEffectView>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cNh-Wc-ADk">
<rect key="frame" x="200" y="113" width="0.0" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="OQ5-hX-qai">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="F37-zt-gM3" secondAttribute="trailing" id="ZRD-3j-s4x"/>
<constraint firstAttribute="bottom" secondItem="F37-zt-gM3" secondAttribute="bottom" id="et1-At-Rgj"/>
<constraint firstItem="F37-zt-gM3" firstAttribute="top" secondItem="ysc-sm-sli" secondAttribute="top" id="jp3-eE-mOy"/>
<constraint firstItem="F37-zt-gM3" firstAttribute="leading" secondItem="ysc-sm-sli" secondAttribute="leading" id="wIo-zP-KId"/>
</constraints>
</view>
<connections>
<outlet property="buttonCancel" destination="FhN-AM-SkI" id="iqV-2E-q7e"/>
<outlet property="buttonCreateLink" destination="L5n-Gw-J27" id="SHV-4l-Red"/>
<outlet property="buttonCreateProxy" destination="01Z-IV-hv1" id="J1v-7J-4fx"/>
<outlet property="textFieldDescription" destination="urj-Xq-TrJ" id="u1w-O0-kI3"/>
<outlet property="textFieldTitle" destination="fJK-Ke-IK3" id="x8p-qx-HX4"/>
</connections>
</viewController>
<customObject id="bZa-dD-d4J" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="250" y="1900"/>
</scene>
<!--Window Controller-->
<scene sceneID="HW6-nV-trE">
<objects>
<windowController storyboardIdentifier="showSelectionWindow" id="t4x-Mh-iya" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="IeW-fo-4yK">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="425" y="462" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
<view key="contentView" id="Oe0-yv-Jcy">
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="t4x-Mh-iya" id="4oO-gI-bd2"/>
</connections>
</window>
<connections>
<segue destination="gOD-Gu-zDG" kind="relationship" relationship="window.shadowedContentViewController" id="KRt-OH-8uc"/>
</connections>
</windowController>
<customObject id="hBK-Bw-dwa" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-374" y="1909"/>
</scene>
</scenes>
<resources>
<image name="Checkmark" width="512" height="512"/>
<image name="IconLinked" width="25" height="25"/>
<image name="IconProxy" width="25" height="25"/>
<image name="Lock" width="30" height="30"/>
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
<image name="plus" catalog="system" width="14" height="13"/>

View File

@@ -47,6 +47,11 @@ class PhpGuard {
// At this point, the version is *not* a match
Log.info("PHP Guard noticed a different PHP version. An alert will be displayed!")
// Exit early if we're running tests; PHP Guard may interfere
if isRunningSwiftUIPreview || isRunningTests {
return
}
Task { @MainActor in
NVAlert()
.withInformation(

View File

@@ -0,0 +1,27 @@
//
// SimpleButton.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 18/02/2026.
// Copyright © 2026 Nico Verbruggen. All rights reserved.
//
import SwiftUI
public struct SimpleButton: View {
public let title: String
public let imageName: String
public let action: () -> Void
public var body: some View {
Button(action: action) {
HStack(spacing: 6) {
Image(imageName)
.resizable()
.scaledToFit()
.frame(width: 14, height: 14) // Standard macOS icon size
Text(title)
}
}
}
}

View File

@@ -0,0 +1,51 @@
//
// SelectDomainView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 18/02/2026.
// Copyright © 2026 Nico Verbruggen. All rights reserved.
//
import SwiftUI
struct SelectDomainTypeView: View {
var onCancel: () -> Void
var onCreateLink: () -> Void
var onCreateProxy: () -> Void
var body: some View {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 15) {
Text("selection.title")
.font(.system(size: 16, weight: .bold, design: .default))
Text("selection.description")
}.padding(25)
Divider()
HStack {
Button("selection.cancel".localized) {
onCancel()
}
Spacer()
SimpleButton(
title: "selection.create_link".localized,
imageName: "IconLinked",
action: { onCreateLink() }
)
SimpleButton(
title: "selection.create_proxy".localized,
imageName: "IconParked",
action: { onCreateProxy() }
)
}
.padding(.all, 20)
.padding(.top, -10)
}
.frame(width: 600)
}
}
#Preview {
SelectDomainTypeView(onCancel: {}, onCreateLink: {}, onCreateProxy: {})
}

View File

@@ -1,5 +1,5 @@
//
// MiniHeaderView.swift
// HeaderView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/06/2022.

View File

@@ -1,5 +1,5 @@
//
// MiniHeaderView.swift
// SectionHeaderView.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 10/06/2022.

View File

@@ -7,6 +7,7 @@
//
import Cocoa
import SwiftUI
class DomainListWindowController: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
@@ -61,18 +62,33 @@ class DomainListWindowController: PMWindowController, NSSearchFieldDelegate, NST
// MARK: - Add a new site
func showSelectionWindow() {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
var hostingController: NSHostingController<SelectDomainTypeView>!
let windowController = storyboard.instantiateController(
withIdentifier: "showSelectionWindow"
) as! NSWindowController
let view = SelectDomainTypeView(
onCancel: {
guard let window = hostingController.view.window,
let parent = window.sheetParent else { return }
parent.endSheet(window, returnCode: .cancel)
},
onCreateLink: {
guard let window = hostingController.view.window,
let parent = window.sheetParent else { return }
parent.endSheet(window, returnCode: .continue)
self.startCreateLinkFlow()
},
onCreateProxy: {
guard let window = hostingController.view.window,
let parent = window.sheetParent else { return }
parent.endSheet(window, returnCode: .continue)
self.startCreateProxyFlow()
}
)
let viewController = windowController.window!
.contentViewController as! SelectionVC
viewController.domainListWC = self
self.window?.beginSheet(windowController.window!)
hostingController = NSHostingController(rootView: view)
hostingController.sizingOptions = .preferredContentSize
let sheetWindow = NSWindow(contentViewController: hostingController)
sheetWindow.styleMask = [.titled, .fullSizeContentView]
self.window?.beginSheet(sheetWindow)
}
func startCreateLinkFlow() {

View File

@@ -1,62 +0,0 @@
//
// SelectionVC.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 14/04/2022.
// Copyright © 2025 Nico Verbruggen. All rights reserved.
//
import Foundation
import Cocoa
class SelectionVC: NSViewController {
weak var domainListWC: DomainListWindowController?
@IBOutlet weak var textFieldTitle: NSTextField!
@IBOutlet weak var textFieldDescription: NSTextField!
@IBOutlet weak var buttonCreateLink: NSButton!
@IBOutlet weak var buttonCreateProxy: NSButton!
@IBOutlet weak var buttonCancel: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
loadStaticLocalisedStrings()
}
override func viewDidAppear() {
view.window?.makeFirstResponder(buttonCreateLink)
}
private func dismissView(outcome: NSApplication.ModalResponse) {
guard let window = self.view.window, let parent = window.sheetParent else { return }
parent.endSheet(window, returnCode: outcome)
}
// MARK: - Localisation
func loadStaticLocalisedStrings() {
textFieldTitle.stringValue = "selection.title".localized
textFieldDescription.stringValue = "selection.description".localized
buttonCancel.title = "selection.cancel".localized
buttonCreateLink.title = "selection.create_link".localized
buttonCreateProxy.title = "selection.create_proxy".localized
}
// MARK: - Outlet Interactions
@IBAction func pressedCreateLink(_ sender: Any) {
self.dismissView(outcome: .continue)
domainListWC?.startCreateLinkFlow()
}
@IBAction func pressedCreateProxy(_ sender: Any) {
self.dismissView(outcome: .continue)
domainListWC?.startCreateProxyFlow()
}
@IBAction func pressedCancel(_ sender: Any) {
self.dismissView(outcome: .cancel)
}
}