mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-03-27 22:40:08 +01:00
✨ Add MarkdownTextView, alert improvements
This commit is contained in:
@@ -182,6 +182,18 @@
|
||||
03BC24A92F51A5F70051292B /* OutputLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24A72F51A5F60051292B /* OutputLine.swift */; };
|
||||
03BC24AA2F51A5F70051292B /* OutputLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24A72F51A5F60051292B /* OutputLine.swift */; };
|
||||
03BC24AB2F51A5F70051292B /* OutputLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24A72F51A5F60051292B /* OutputLine.swift */; };
|
||||
03BC24AD2F51B33B0051292B /* MarkdownTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */; };
|
||||
03BC24AE2F51B33B0051292B /* MarkdownTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */; };
|
||||
03BC24AF2F51B33B0051292B /* MarkdownTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */; };
|
||||
03BC24B02F51B33B0051292B /* MarkdownTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */; };
|
||||
03BC24B62F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */; };
|
||||
03BC24B72F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */; };
|
||||
03BC24B82F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */; };
|
||||
03BC24B92F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */; };
|
||||
03BC24BB2F51C55E0051292B /* CodeBlockTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */; };
|
||||
03BC24BC2F51C55E0051292B /* CodeBlockTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */; };
|
||||
03BC24BD2F51C55E0051292B /* CodeBlockTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */; };
|
||||
03BC24BE2F51C55E0051292B /* CodeBlockTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */; };
|
||||
03BFF5272E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF5282E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03BFF5262E312C39007F96FA /* Startup+Timers.swift */; };
|
||||
@@ -234,10 +246,8 @@
|
||||
1C91CB7232F304AA7F6296CB /* StartupAlertButtonRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBEBCF2527961D5D13F68A8 /* StartupAlertButtonRow.swift */; };
|
||||
1D1173AE9AC9C8315E899D06 /* StartupAlertViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B900AAECE035041E9BE09DCF /* StartupAlertViewModel.swift */; };
|
||||
25250C95E34D20ED4C91C320 /* CommandHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31503E15DADA980998F0F5A2 /* CommandHistoryView.swift */; };
|
||||
288730259CFF37771C773EA2 /* MarkdownText.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C0BBF856B022EA51606492 /* MarkdownText.swift */; };
|
||||
2F9D926CBFE51F3B21A76536 /* StartupOutputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E531FA9DE31575AF518941 /* StartupOutputView.swift */; };
|
||||
4181B8F1C0ED930B2C5E5532 /* CommandHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31503E15DADA980998F0F5A2 /* CommandHistoryView.swift */; };
|
||||
4AAB0E1F7037BEFAE0037AFF /* MarkdownText.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C0BBF856B022EA51606492 /* MarkdownText.swift */; };
|
||||
5420395926135DC100FB00FA /* PreferencesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PreferencesVC.swift */; };
|
||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||
5489625828312FAD004F647A /* CreatedFromFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5489625728312FAD004F647A /* CreatedFromFile.swift */; };
|
||||
@@ -280,8 +290,6 @@
|
||||
A42F1F6EF455F42769E63FF5 /* StartupAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51912F2DA8FD7D8A1578644 /* StartupAlertWindowController.swift */; };
|
||||
B54FBBEFE5E1782AFF7C434E /* StartupAlertHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF8DC9BC21B1EFB1F6CBBA5 /* StartupAlertHeaderView.swift */; };
|
||||
BB3AF45BED2C82AE27E86107 /* CommandHistoryWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51118AFDD02AA1B7D8C9278 /* CommandHistoryWindowController.swift */; };
|
||||
BFF65E73753B67381C23D65E /* MarkdownText.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C0BBF856B022EA51606492 /* MarkdownText.swift */; };
|
||||
C277F9F96197AD07FA91E3CB /* MarkdownText.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C0BBF856B022EA51606492 /* MarkdownText.swift */; };
|
||||
C40175B82903108900763A68 /* ValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40175B72903108900763A68 /* ValetInteractor.swift */; };
|
||||
C40175B92903108900763A68 /* ValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40175B72903108900763A68 /* ValetInteractor.swift */; };
|
||||
C40175BA2903108900763A68 /* ValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40175B72903108900763A68 /* ValetInteractor.swift */; };
|
||||
@@ -1178,6 +1186,9 @@
|
||||
03B947DD2F43691D00B6F899 /* TestURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURL.swift; sourceTree = "<group>"; };
|
||||
03B947E02F436A6700B6F899 /* Binaries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Binaries.swift; sourceTree = "<group>"; };
|
||||
03BC24A72F51A5F60051292B /* OutputLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputLine.swift; sourceTree = "<group>"; };
|
||||
03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTextView.swift; sourceTree = "<group>"; };
|
||||
03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTextViewRepresentable.swift; sourceTree = "<group>"; };
|
||||
03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeBlockTextView.swift; sourceTree = "<group>"; };
|
||||
03BFF5262E312C39007F96FA /* Startup+Timers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Startup+Timers.swift"; sourceTree = "<group>"; };
|
||||
03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Driver.swift"; sourceTree = "<group>"; };
|
||||
03C099432EA15C8B00B76D43 /* Container+Real.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+Real.swift"; sourceTree = "<group>"; };
|
||||
@@ -1212,7 +1223,6 @@
|
||||
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
|
||||
68063C53EE0F48E91ADFE92F /* StartupFixCommandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupFixCommandView.swift; sourceTree = "<group>"; };
|
||||
7EF8DC9BC21B1EFB1F6CBBA5 /* StartupAlertHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupAlertHeaderView.swift; sourceTree = "<group>"; };
|
||||
A4C0BBF856B022EA51606492 /* MarkdownText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownText.swift; sourceTree = "<group>"; };
|
||||
B900AAECE035041E9BE09DCF /* StartupAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupAlertViewModel.swift; sourceTree = "<group>"; };
|
||||
C40175B72903108900763A68 /* ValetInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetInteractor.swift; sourceTree = "<group>"; };
|
||||
C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuItemExtension.swift; sourceTree = "<group>"; };
|
||||
@@ -1633,6 +1643,36 @@
|
||||
path = Conditions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03BC24B12F51B4550051292B /* Buttons */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
031D747B2F46225600D4FF48 /* SimpleButton.swift */,
|
||||
C451AFF52969E40F0078E617 /* HelpButton.swift */,
|
||||
C4EA3C462BA4F947007B0BA7 /* CustomButtonStyles.swift */,
|
||||
);
|
||||
path = Buttons;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03BC24B22F51B4690051292B /* Markdown */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */,
|
||||
03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */,
|
||||
03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */,
|
||||
);
|
||||
path = Markdown;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03BC24B32F51B4790051292B /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03EC943B2F47297100231276 /* ErrorView.swift */,
|
||||
C43B8FD42BA9BAD3000C02BE /* UnavailableContentView.swift */,
|
||||
C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
03BFF1D12E3CF4F2004C56A9 /* Provision */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -2405,14 +2445,10 @@
|
||||
C4B609162853AA9A00C95265 /* Common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
03EC943B2F47297100231276 /* ErrorView.swift */,
|
||||
A4C0BBF856B022EA51606492 /* MarkdownText.swift */,
|
||||
031D747B2F46225600D4FF48 /* SimpleButton.swift */,
|
||||
C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */,
|
||||
C451AFF52969E40F0078E617 /* HelpButton.swift */,
|
||||
C4EA3C462BA4F947007B0BA7 /* CustomButtonStyles.swift */,
|
||||
C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */,
|
||||
C43B8FD42BA9BAD3000C02BE /* UnavailableContentView.swift */,
|
||||
03BC24B22F51B4690051292B /* Markdown */,
|
||||
03BC24B12F51B4550051292B /* Buttons */,
|
||||
03BC24B32F51B4790051292B /* Views */,
|
||||
);
|
||||
path = Common;
|
||||
sourceTree = "<group>";
|
||||
@@ -3004,6 +3040,7 @@
|
||||
C4998F0A2617633900B2526E /* PreferencesWindowController.swift in Sources */,
|
||||
C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */,
|
||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
||||
03BC24AF2F51B33B0051292B /* MarkdownTextView.swift in Sources */,
|
||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||
C4EB53E728553117006F9937 /* ArrayExtension.swift in Sources */,
|
||||
5420395926135DC100FB00FA /* PreferencesVC.swift in Sources */,
|
||||
@@ -3088,7 +3125,6 @@
|
||||
7E19EE251E743286BC95EBA3 /* StartupAlertViewModel.swift in Sources */,
|
||||
C57636BF2B81A9D147B28D1C /* StartupAlertView.swift in Sources */,
|
||||
148796E43977CCBC22AAB5CC /* StartupAlertWindowController.swift in Sources */,
|
||||
288730259CFF37771C773EA2 /* MarkdownText.swift in Sources */,
|
||||
5D754EAE78FF1E264AD02A46 /* StartupAlertHeaderView.swift in Sources */,
|
||||
F644E222C9D096B94E363E60 /* StartupFixCommandView.swift in Sources */,
|
||||
2F9D926CBFE51F3B21A76536 /* StartupOutputView.swift in Sources */,
|
||||
@@ -3104,6 +3140,7 @@
|
||||
C495F5AF28A42E080087F70A /* EnvironmentCheck.swift in Sources */,
|
||||
C46EBC4428DB95F0007ACC74 /* ShellProtocol.swift in Sources */,
|
||||
C46EBC4C28DB95F0007ACC74 /* TrackedShell.swift in Sources */,
|
||||
03BC24BB2F51C55E0051292B /* CodeBlockTextView.swift in Sources */,
|
||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||
@@ -3179,6 +3216,7 @@
|
||||
039E1D7C2E5F0F300072D13D /* ValetUpgrader.swift in Sources */,
|
||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||
036C390D2E5C8CC5008DAEDF /* PackagistP2Response.swift in Sources */,
|
||||
03BC24B62F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */,
|
||||
031D74842F46225C00D4FF48 /* SelectDomainTypeView.swift in Sources */,
|
||||
C4D5CFCA27E0F9CD00035329 /* NginxConfigurationFile.swift in Sources */,
|
||||
C485707028BF452300539B36 /* PhpDoctorWindowController.swift in Sources */,
|
||||
@@ -3420,6 +3458,7 @@
|
||||
C471E7DB28F9BA8F0021E251 /* RealShell.swift in Sources */,
|
||||
C471E7FA28F9BA8F0021E251 /* TrackedShell.swift in Sources */,
|
||||
C471E7FF28F9BAD10021E251 /* Xdebug.swift in Sources */,
|
||||
03BC24BD2F51C55E0051292B /* CodeBlockTextView.swift in Sources */,
|
||||
037F441D2EDB9195002EBF75 /* ConfigWatchManager.swift in Sources */,
|
||||
C409349F298EE8E900D25014 /* AppUpdater.swift in Sources */,
|
||||
03BFF5292E312C3D007F96FA /* Startup+Timers.swift in Sources */,
|
||||
@@ -3454,11 +3493,11 @@
|
||||
C471E82028F9BB290021E251 /* NginxConfigurationFile.swift in Sources */,
|
||||
032DAC2D2E8BEB6B0018E01C /* WebApiProtocol.swift in Sources */,
|
||||
C4513F902B13E2E6001AD760 /* PhpExtensionManagerWindowController.swift in Sources */,
|
||||
03BC24B82F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */,
|
||||
03EC943F2F47297B00231276 /* ErrorView.swift in Sources */,
|
||||
FEA10615F601BEC79BBDA45B /* StartupAlertViewModel.swift in Sources */,
|
||||
9297DB3F2A8903D70E1D6426 /* StartupAlertView.swift in Sources */,
|
||||
8A8AB3D1A6DAE99C91DFBEFF /* StartupAlertWindowController.swift in Sources */,
|
||||
4AAB0E1F7037BEFAE0037AFF /* MarkdownText.swift in Sources */,
|
||||
95BB6DF72FB35A5A8F4E24E7 /* StartupAlertHeaderView.swift in Sources */,
|
||||
632A06CFEEC7A76C34992F38 /* StartupFixCommandView.swift in Sources */,
|
||||
FF788D404D414CC970301C81 /* StartupOutputView.swift in Sources */,
|
||||
@@ -3474,6 +3513,7 @@
|
||||
C4821C5C2C2DEDE200357A68 /* AppMenu.swift in Sources */,
|
||||
0386B0B42ED36C3D00CA6795 /* Locked.swift in Sources */,
|
||||
0392CDED2EB25371009176DA /* SecurePopoverView.swift in Sources */,
|
||||
03BC24B02F51B33B0051292B /* MarkdownTextView.swift in Sources */,
|
||||
C471E81328F9BAE80021E251 /* XibLoadable.swift in Sources */,
|
||||
C4D3661C291173EA006BD146 /* DictionaryExtension.swift in Sources */,
|
||||
C4B79ECD29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */,
|
||||
@@ -3606,7 +3646,6 @@
|
||||
03BC24A82F51A5F70051292B /* OutputLine.swift in Sources */,
|
||||
88DD3A08057A23955913FB70 /* StartupAlertView.swift in Sources */,
|
||||
A42F1F6EF455F42769E63FF5 /* StartupAlertWindowController.swift in Sources */,
|
||||
C277F9F96197AD07FA91E3CB /* MarkdownText.swift in Sources */,
|
||||
B54FBBEFE5E1782AFF7C434E /* StartupAlertHeaderView.swift in Sources */,
|
||||
F4B7E3F9B46C5AD8B8B3FEBA /* StartupFixCommandView.swift in Sources */,
|
||||
FB95EF9B99E4ED160E751E44 /* StartupOutputView.swift in Sources */,
|
||||
@@ -3624,10 +3663,12 @@
|
||||
C471E8E128F9BB8F0021E251 /* PresetHelper.swift in Sources */,
|
||||
038131492F4E00D500653177 /* TrackedTestableCommand.swift in Sources */,
|
||||
031D747D2F46225600D4FF48 /* SimpleButton.swift in Sources */,
|
||||
03BC24AD2F51B33B0051292B /* MarkdownTextView.swift in Sources */,
|
||||
038131442F4E00C300653177 /* TrackedTestableShell.swift in Sources */,
|
||||
C471E8E228F9BB8F0021E251 /* WarningView.swift in Sources */,
|
||||
C471E8E328F9BB8F0021E251 /* PhpDoctorView.swift in Sources */,
|
||||
0A1A6208D3DD2495FBD8569B /* CommandHistoryView.swift in Sources */,
|
||||
03BC24B92F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */,
|
||||
C471E8E428F9BB8F0021E251 /* NoWarningsView.swift in Sources */,
|
||||
C471E8E528F9BB8F0021E251 /* OnboardingView.swift in Sources */,
|
||||
C4B79EBF29CA38DB00A483EE /* BrewCommand.swift in Sources */,
|
||||
@@ -3708,6 +3749,7 @@
|
||||
C471E7CA28F9BA480021E251 /* TestableFileSystem.swift in Sources */,
|
||||
C471E7DD28F9BAA30021E251 /* CommandProtocol.swift in Sources */,
|
||||
C471E7FC28F9BAA30021E251 /* TrackedCommand.swift in Sources */,
|
||||
03BC24BC2F51C55E0051292B /* CodeBlockTextView.swift in Sources */,
|
||||
C471E7D128F9BA630021E251 /* RealFileSystem.swift in Sources */,
|
||||
033D459B2B0D4EC600070080 /* InstallPhpExtensionCommand.swift in Sources */,
|
||||
C471E82B28F9BB340021E251 /* Valet.swift in Sources */,
|
||||
@@ -3746,6 +3788,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */,
|
||||
03BC24B72F51C5230051292B /* MarkdownTextViewRepresentable.swift in Sources */,
|
||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||
C485707128BF452E00539B36 /* WarningManager.swift in Sources */,
|
||||
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||
@@ -3807,7 +3850,6 @@
|
||||
EE4FA5D074AD5C9D09C25A55 /* StartupAlertViewModel.swift in Sources */,
|
||||
959BE2F3A5743862AC8D1E7D /* StartupAlertView.swift in Sources */,
|
||||
E56493248D049CA2EBB82B21 /* StartupAlertWindowController.swift in Sources */,
|
||||
BFF65E73753B67381C23D65E /* MarkdownText.swift in Sources */,
|
||||
56306F63B84C9E99C2C26375 /* StartupAlertHeaderView.swift in Sources */,
|
||||
67E571F5200F9C57CC673499 /* StartupFixCommandView.swift in Sources */,
|
||||
CCF5FE7B49F3B0BC77BAF7D7 /* StartupOutputView.swift in Sources */,
|
||||
@@ -3910,6 +3952,7 @@
|
||||
C4E2E86528FC2F1B003B070C /* XCPMApplication.swift in Sources */,
|
||||
C489E0BC2A220A4200323F5E /* FakeBrewFormulaeHandler.swift in Sources */,
|
||||
036C390B2E5C8CC5008DAEDF /* PackagistP2Response.swift in Sources */,
|
||||
03BC24AE2F51B33B0051292B /* MarkdownTextView.swift in Sources */,
|
||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||
03DAD3A62EB3B08F003417BD /* DomainListVC+Certs.swift in Sources */,
|
||||
C4B79ECC29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */,
|
||||
@@ -4012,6 +4055,7 @@
|
||||
0381314A2F4E00D500653177 /* TrackedTestableCommand.swift in Sources */,
|
||||
C4551657297AED18009B8466 /* ValetRcTest.swift in Sources */,
|
||||
C464ADAD275A7A3F003FCD53 /* DomainListWindowController.swift in Sources */,
|
||||
03BC24BE2F51C55E0051292B /* CodeBlockTextView.swift in Sources */,
|
||||
C40C7F1F2772136000DDDCDC /* PhpEnvironments.swift in Sources */,
|
||||
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */,
|
||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// CodeBlockTextView.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 27/02/2026.
|
||||
// Copyright © 2026 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
/// Note: Written with the help of an LLM.
|
||||
class CodeBlockTextView: NSTextView {
|
||||
private let codePaddingX: CGFloat = 4
|
||||
private let codePaddingY: CGFloat = 1
|
||||
private let codeCornerRadius: CGFloat = 4
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
drawCodeBackgrounds()
|
||||
super.draw(dirtyRect)
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: NSSize {
|
||||
guard let textContainer, let layoutManager else {
|
||||
return super.intrinsicContentSize
|
||||
}
|
||||
layoutManager.ensureLayout(for: textContainer)
|
||||
let rect = layoutManager.usedRect(for: textContainer)
|
||||
return NSSize(width: NSView.noIntrinsicMetric, height: ceil(rect.height))
|
||||
}
|
||||
|
||||
private func drawCodeBackgrounds() {
|
||||
guard let textStorage, let layoutManager, let textContainer else { return }
|
||||
|
||||
let codeSpanKey = MarkdownTextViewRepresentable.codeSpanKey
|
||||
let appColor = NSColor(named: "AppColor") ?? .systemBlue
|
||||
|
||||
textStorage.enumerateAttribute(codeSpanKey, in: NSRange(location: 0, length: textStorage.length)) { value, range, _ in
|
||||
guard value != nil else { return }
|
||||
|
||||
// Get the glyph range and bounding rect for this code span
|
||||
let glyphRange = layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
|
||||
let textRect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
|
||||
|
||||
// Offset by text container inset
|
||||
let rect = textRect.offsetBy(dx: textContainerInset.width, dy: textContainerInset.height)
|
||||
let paddedRect = rect.insetBy(dx: -codePaddingX, dy: -codePaddingY)
|
||||
let path = NSBezierPath(roundedRect: paddedRect, xRadius: codeCornerRadius, yRadius: codeCornerRadius)
|
||||
|
||||
// Fill
|
||||
appColor.withAlphaComponent(0.15).setFill()
|
||||
path.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
39
phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextView.swift
Normal file
39
phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextView.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// MarkdownTextView.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 27/02/2026.
|
||||
// Copyright © 2026 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MarkdownTextView: View {
|
||||
let string: String
|
||||
let fontSize: CGFloat
|
||||
|
||||
init(_ string: String, fontSize: CGFloat = 12) {
|
||||
self.string = string
|
||||
self.fontSize = fontSize
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
MarkdownTextViewRepresentable(string: string, fontSize: fontSize)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
#Preview("Inline code") {
|
||||
MarkdownTextView("startup.errors.php_binary.desc".localized(
|
||||
"/opt/homebrew/bin/php"
|
||||
))
|
||||
.frame(width: 460)
|
||||
.padding()
|
||||
}
|
||||
|
||||
#Preview("No code") {
|
||||
MarkdownTextView("startup.errors.valet_version_not_supported.desc".localized)
|
||||
.frame(width: 460)
|
||||
.padding()
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// MarkdownTextViewRepresentable.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 27/02/2026.
|
||||
// Copyright © 2026 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
/// Note: Written with the help of an LLM.
|
||||
struct MarkdownTextViewRepresentable: NSViewRepresentable {
|
||||
let string: String
|
||||
let fontSize: CGFloat
|
||||
|
||||
func makeNSView(context: Context) -> CodeBlockTextView {
|
||||
let textView = CodeBlockTextView()
|
||||
textView.isEditable = false
|
||||
textView.isSelectable = true
|
||||
textView.drawsBackground = false
|
||||
textView.textContainerInset = .zero
|
||||
textView.textContainer?.lineFragmentPadding = 0
|
||||
textView.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
textView.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
configure(textView)
|
||||
return textView
|
||||
}
|
||||
|
||||
func updateNSView(_ textView: CodeBlockTextView, context: Context) {
|
||||
configure(textView)
|
||||
}
|
||||
|
||||
private func configure(_ textView: CodeBlockTextView) {
|
||||
let attributed = Self.buildAttributedString(from: string, fontSize: fontSize)
|
||||
textView.textStorage?.setAttributedString(attributed)
|
||||
textView.invalidateIntrinsicContentSize()
|
||||
}
|
||||
|
||||
// MARK: - Attributed String Builder
|
||||
|
||||
static func buildAttributedString(from string: String, fontSize: CGFloat) -> NSAttributedString {
|
||||
let result = NSMutableAttributedString()
|
||||
let font = NSFont.systemFont(ofSize: fontSize)
|
||||
let codeFont = NSFont.monospacedSystemFont(ofSize: fontSize - 1, weight: .regular)
|
||||
|
||||
// Add additional spacing for code blocks w/ thin spaces
|
||||
let thinSpace = "\u{2009}\u{2009}\u{2009}"
|
||||
|
||||
let defaultAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: font,
|
||||
.foregroundColor: NSColor.labelColor
|
||||
]
|
||||
|
||||
var current = string.startIndex
|
||||
|
||||
while let backtickStart = string[current...].firstIndex(of: "`") {
|
||||
if current < backtickStart {
|
||||
result.append(NSAttributedString(
|
||||
string: String(string[current..<backtickStart]),
|
||||
attributes: defaultAttributes
|
||||
))
|
||||
}
|
||||
|
||||
let afterBacktick = string.index(after: backtickStart)
|
||||
if afterBacktick < string.endIndex,
|
||||
let backtickEnd = string[afterBacktick...].firstIndex(of: "`") {
|
||||
// Thin space before
|
||||
result.append(NSAttributedString(string: thinSpace, attributes: defaultAttributes))
|
||||
|
||||
// Code span with marker attribute
|
||||
let codeAttributes: [NSAttributedString.Key: Any] = [
|
||||
.font: codeFont,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
Self.codeSpanKey: true
|
||||
]
|
||||
result.append(NSAttributedString(
|
||||
string: String(string[afterBacktick..<backtickEnd]),
|
||||
attributes: codeAttributes
|
||||
))
|
||||
|
||||
// Thin space after
|
||||
result.append(NSAttributedString(string: thinSpace, attributes: defaultAttributes))
|
||||
|
||||
current = string.index(after: backtickEnd)
|
||||
} else {
|
||||
result.append(NSAttributedString(
|
||||
string: String(string[backtickStart...]),
|
||||
attributes: defaultAttributes
|
||||
))
|
||||
current = string.endIndex
|
||||
}
|
||||
}
|
||||
|
||||
if current < string.endIndex {
|
||||
result.append(NSAttributedString(
|
||||
string: String(string[current...]),
|
||||
attributes: defaultAttributes
|
||||
))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
static let codeSpanKey = NSAttributedString.Key("PHPMonitorCodeSpan")
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// MarkdownText.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 25/02/2026.
|
||||
// Copyright © 2026 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension Text {
|
||||
init(markdown string: String, fontSize: CGFloat? = nil) {
|
||||
if var attributed = try? AttributedString(
|
||||
markdown: string,
|
||||
options: .init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
) {
|
||||
for run in attributed.runs where ((run.inlinePresentationIntent?.contains(.code)) != nil) {
|
||||
attributed[run.range].backgroundColor = Color(nsColor: .quaternaryLabelColor)
|
||||
}
|
||||
self.init(attributed)
|
||||
} else {
|
||||
self.init(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,12 @@ struct StartupAlertHeaderView: View {
|
||||
.frame(width: 60, height: 60)
|
||||
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(markdown: titleText)
|
||||
Text(titleText)
|
||||
.font(.system(size: 15, weight: .bold))
|
||||
.textSelection(.enabled)
|
||||
Text(markdown: subtitleText)
|
||||
.font(.system(size: 12))
|
||||
|
||||
MarkdownTextView(subtitleText, fontSize: 13)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -11,6 +11,18 @@ import SwiftUI
|
||||
struct StartupAlertView: View {
|
||||
@ObservedObject var viewModel: StartupAlertViewModel
|
||||
|
||||
/// Whether the bottom section (description text and/or past output) has content to display.
|
||||
/// This is used to conditionally show the section and its divider,
|
||||
/// avoiding empty padded sections and double dividers.
|
||||
private var hasBottomContent: Bool {
|
||||
let hasDescription = !viewModel.check.descriptionText.isEmpty && viewModel.state == .idle
|
||||
|
||||
let hasOutput = !viewModel.outputLines.isEmpty
|
||||
&& (viewModel.state == .idle || viewModel.state == .completed)
|
||||
|
||||
return hasDescription || hasOutput
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
StartupAlertHeaderView(
|
||||
@@ -18,39 +30,45 @@ struct StartupAlertView: View {
|
||||
subtitleText: viewModel.check.subtitleText
|
||||
)
|
||||
|
||||
Divider()
|
||||
if viewModel.state == .running || (viewModel.hasFix && viewModel.state == .idle) {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
if viewModel.state == .running {
|
||||
StartupOutputView(
|
||||
lines: viewModel.outputLines,
|
||||
isRunning: true
|
||||
)
|
||||
} else if viewModel.hasFix, viewModel.state == .idle {
|
||||
StartupFixCommandView(
|
||||
command: viewModel.check.fixDescription ?? ""
|
||||
)
|
||||
}
|
||||
|
||||
if !viewModel.check.descriptionText.isEmpty,
|
||||
viewModel.state == .idle {
|
||||
Text(markdown: viewModel.check.descriptionText, fontSize: 12)
|
||||
.font(.system(size: 12))
|
||||
.foregroundStyle(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
if !viewModel.outputLines.isEmpty,
|
||||
viewModel.state == .idle || viewModel.state == .completed {
|
||||
StartupOutputView(
|
||||
lines: viewModel.outputLines,
|
||||
isRunning: false
|
||||
)
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
if viewModel.state == .running {
|
||||
StartupOutputView(
|
||||
lines: viewModel.outputLines,
|
||||
isRunning: true
|
||||
)
|
||||
} else {
|
||||
StartupFixCommandView(
|
||||
command: viewModel.check.fixDescription ?? ""
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
if hasBottomContent {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
if !viewModel.check.descriptionText.isEmpty,
|
||||
viewModel.state == .idle {
|
||||
MarkdownTextView(viewModel.check.descriptionText, fontSize: 12)
|
||||
}
|
||||
|
||||
if !viewModel.outputLines.isEmpty,
|
||||
viewModel.state == .idle || viewModel.state == .completed {
|
||||
StartupOutputView(
|
||||
lines: viewModel.outputLines,
|
||||
isRunning: false
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.padding(20)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
|
||||
@@ -93,17 +93,17 @@ class StartupAlertViewModel: ObservableObject {
|
||||
|
||||
@MainActor private func pass() {
|
||||
self.state = .completed
|
||||
self.outputLines.append(OutputLine(text: "\nFix applied successfully! Continuing...", stream: .stdOut))
|
||||
self.outputLines.append(OutputLine(text: "---\nFix applied successfully! Continuing...", stream: .stdOut))
|
||||
}
|
||||
|
||||
@MainActor private func fail() {
|
||||
self.state = .idle
|
||||
self.outputLines.append(OutputLine(text: "\nFix did not resolve the issue.", stream: .stdErr))
|
||||
self.outputLines.append(OutputLine(text: "---\nFix did not resolve the issue.", stream: .stdErr))
|
||||
}
|
||||
|
||||
@MainActor private func errorAndIdle(_ error: Error) {
|
||||
self.state = .idle
|
||||
self.outputLines.append(OutputLine(text: "\nError: \(error.localizedDescription)", stream: .stdErr))
|
||||
self.outputLines.append(OutputLine(text: "---\nError: \(error.localizedDescription)", stream: .stdErr))
|
||||
}
|
||||
|
||||
// MARK: - Alert Outcomes
|
||||
|
||||
@@ -663,8 +663,8 @@ You can do this by running `composer global update` in your terminal. After that
|
||||
"alert.homebrew_missing.quit" = "Quit";
|
||||
|
||||
// PHP binary not found
|
||||
"startup.errors.php_binary.title" = "PHP is not correctly installed";
|
||||
"startup.errors.php_binary.subtitle" = "You must install PHP via Homebrew. The app will not work correctly until you resolve this issue.";
|
||||
"startup.errors.php_binary.title" = "PHP is not correctly linked";
|
||||
"startup.errors.php_binary.subtitle" = "A valid PHP installation was found, but it is not linked. The app will not work correctly until you resolve this issue.";
|
||||
"startup.errors.php_binary.desc" = "Usually, running `brew link php` in your Terminal will resolve this issue.\n\nTo diagnose what is wrong, you can try running `which php` in your Terminal, it should return `%@`.";
|
||||
|
||||
// Invalid brew info php output
|
||||
@@ -716,12 +716,12 @@ If you are seeing this message but are confused why this folder has gone missing
|
||||
|
||||
// Brew & sudoers
|
||||
"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 `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.desc" = "If you keep seeing this error, it is possible that there is a permission issue where PHP Monitor cannot validate the file, which can usually be resolved by running: `sudo chmod +r /private/etc/sudoers.d/brew`";
|
||||
|
||||
// Valet & sudoers
|
||||
"startup.errors.sudoers_valet.title" = "Valet has not been added to sudoers.d";
|
||||
"startup.errors.sudoers_valet.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. If you did this before, please run `sudo valet trust` again.";
|
||||
"startup.errors.sudoers_valet.subtitle" = "You must run `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_valet.desc" = "If you keep seeing this error, it is possible that there is a permission issue where PHP Monitor cannot validate the file, which can usually be resolved by running: `sudo chmod +r /private/etc/sudoers.d/valet`";
|
||||
|
||||
// Platform issue detected
|
||||
|
||||
Reference in New Issue
Block a user