diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 03143fae..a7f0b758 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -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 = ""; }; 03B947E02F436A6700B6F899 /* Binaries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Binaries.swift; sourceTree = ""; }; 03BC24A72F51A5F60051292B /* OutputLine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputLine.swift; sourceTree = ""; }; + 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTextView.swift; sourceTree = ""; }; + 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownTextViewRepresentable.swift; sourceTree = ""; }; + 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodeBlockTextView.swift; sourceTree = ""; }; 03BFF5262E312C39007F96FA /* Startup+Timers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Startup+Timers.swift"; sourceTree = ""; }; 03BFF52B2E313240007F96FA /* StatusMenu+Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusMenu+Driver.swift"; sourceTree = ""; }; 03C099432EA15C8B00B76D43 /* Container+Real.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Container+Real.swift"; sourceTree = ""; }; @@ -1212,7 +1223,6 @@ 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = ""; }; 68063C53EE0F48E91ADFE92F /* StartupFixCommandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupFixCommandView.swift; sourceTree = ""; }; 7EF8DC9BC21B1EFB1F6CBBA5 /* StartupAlertHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupAlertHeaderView.swift; sourceTree = ""; }; - A4C0BBF856B022EA51606492 /* MarkdownText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownText.swift; sourceTree = ""; }; B900AAECE035041E9BE09DCF /* StartupAlertViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartupAlertViewModel.swift; sourceTree = ""; }; C40175B72903108900763A68 /* ValetInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetInteractor.swift; sourceTree = ""; }; C40508B028ADAB44008FAC1F /* NSMenuItemExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuItemExtension.swift; sourceTree = ""; }; @@ -1633,6 +1643,36 @@ path = Conditions; sourceTree = ""; }; + 03BC24B12F51B4550051292B /* Buttons */ = { + isa = PBXGroup; + children = ( + 031D747B2F46225600D4FF48 /* SimpleButton.swift */, + C451AFF52969E40F0078E617 /* HelpButton.swift */, + C4EA3C462BA4F947007B0BA7 /* CustomButtonStyles.swift */, + ); + path = Buttons; + sourceTree = ""; + }; + 03BC24B22F51B4690051292B /* Markdown */ = { + isa = PBXGroup; + children = ( + 03BC24AC2F51B33B0051292B /* MarkdownTextView.swift */, + 03BC24B52F51C51F0051292B /* MarkdownTextViewRepresentable.swift */, + 03BC24BA2F51C5560051292B /* CodeBlockTextView.swift */, + ); + path = Markdown; + sourceTree = ""; + }; + 03BC24B32F51B4790051292B /* Views */ = { + isa = PBXGroup; + children = ( + 03EC943B2F47297100231276 /* ErrorView.swift */, + C43B8FD42BA9BAD3000C02BE /* UnavailableContentView.swift */, + C48DDD0C29C75C9E00D032D9 /* BlockingOverlayView.swift */, + ); + path = Views; + sourceTree = ""; + }; 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 = ""; @@ -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 */, diff --git a/phpmon/Domain/SwiftUI/Common/CustomButtonStyles.swift b/phpmon/Domain/SwiftUI/Common/Buttons/CustomButtonStyles.swift similarity index 100% rename from phpmon/Domain/SwiftUI/Common/CustomButtonStyles.swift rename to phpmon/Domain/SwiftUI/Common/Buttons/CustomButtonStyles.swift diff --git a/phpmon/Domain/SwiftUI/Common/HelpButton.swift b/phpmon/Domain/SwiftUI/Common/Buttons/HelpButton.swift similarity index 100% rename from phpmon/Domain/SwiftUI/Common/HelpButton.swift rename to phpmon/Domain/SwiftUI/Common/Buttons/HelpButton.swift diff --git a/phpmon/Domain/SwiftUI/Common/SimpleButton.swift b/phpmon/Domain/SwiftUI/Common/Buttons/SimpleButton.swift similarity index 100% rename from phpmon/Domain/SwiftUI/Common/SimpleButton.swift rename to phpmon/Domain/SwiftUI/Common/Buttons/SimpleButton.swift diff --git a/phpmon/Domain/SwiftUI/Common/Markdown/CodeBlockTextView.swift b/phpmon/Domain/SwiftUI/Common/Markdown/CodeBlockTextView.swift new file mode 100644 index 00000000..4f6dceef --- /dev/null +++ b/phpmon/Domain/SwiftUI/Common/Markdown/CodeBlockTextView.swift @@ -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() + } + } +} diff --git a/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextView.swift b/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextView.swift new file mode 100644 index 00000000..e0ce9709 --- /dev/null +++ b/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextView.swift @@ -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() +} diff --git a/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextViewRepresentable.swift b/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextViewRepresentable.swift new file mode 100644 index 00000000..8433740a --- /dev/null +++ b/phpmon/Domain/SwiftUI/Common/Markdown/MarkdownTextViewRepresentable.swift @@ -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..