├── .gitignore ├── LICENSE ├── OWNERS ├── Podman Desktop.xcodeproj └── project.pbxproj ├── Podman Desktop ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── debug.imageset │ │ ├── Contents.json │ │ └── debug.svg │ ├── podman-1.imageset │ │ ├── Contents.json │ │ └── layer1-2.svg │ ├── podman-large.imageset │ │ ├── podman.svg │ │ └── Contents.json │ ├── podman-logo-sm.imageset │ │ ├── Contents.json │ │ └── podman-logo-sm.png │ ├── podman-logo.imageset │ │ ├── Contents.json │ │ └── podman-logo.svg │ ├── podman-purple.colorset │ │ └── Contents.json │ ├── reboot.imageset │ │ ├── Contents.json │ │ └── reboot.svg │ ├── selkie-artwork.imageset │ │ ├── Contents.json │ │ └── selkie-artwork.svg │ ├── toggle-off.colorset │ │ └── Contents.json │ └── toggle-on.colorset │ │ └── Contents.json ├── Models │ ├── Errors.swift │ ├── Machine.swift │ ├── MenuBarController.swift │ ├── Shell.swift │ └── ViewRouter.swift ├── Podman_Desktop.entitlements ├── Podman_DesktopApp.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json └── Views │ ├── BodyView.swift │ ├── ContentView.swift │ ├── HeaderView.swift │ ├── MachineInitView.swift │ ├── MachineSelectView.swift │ ├── MenuBar.swift │ ├── SelectMachineView.swift │ └── SettingsView.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | 93 | #macOS gitignore 94 | # General 95 | .DS_Store 96 | .AppleDouble 97 | .LSOverride 98 | 99 | # Icon must end with two \r 100 | Icon 101 | 102 | # Thumbnails 103 | ._* 104 | 105 | # Files that might appear in the root of a volume 106 | .DocumentRevisions-V100 107 | .fseventsd 108 | .Spotlight-V100 109 | .TemporaryItems 110 | .Trashes 111 | .VolumeIcon.icns 112 | .com.apple.timemachine.donotpresent 113 | 114 | # Directories potentially created on remote AFP share 115 | .AppleDB 116 | .AppleDesktop 117 | Network Trash Folder 118 | Temporary Items 119 | .apdisk -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - TomSweeneyRedHat 3 | - ashley-cui 4 | - baude 5 | - jwhonce 6 | - mheon 7 | - rhatdan 8 | reviewers: 9 | - baude 10 | - lsm5 11 | - mheon 12 | - rhatdan 13 | - umohnani8 14 | - vrothberg 15 | -------------------------------------------------------------------------------- /Podman Desktop.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1425F9D527A7E3E200057FDD /* ViewRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1425F9D427A7E3E200057FDD /* ViewRouter.swift */; }; 11 | 1464F9E127544392006A6813 /* Podman_DesktopApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1464F9E027544392006A6813 /* Podman_DesktopApp.swift */; }; 12 | 1464F9E327544392006A6813 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1464F9E227544392006A6813 /* ContentView.swift */; }; 13 | 1464F9E527544393006A6813 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1464F9E427544393006A6813 /* Assets.xcassets */; }; 14 | 1464F9E827544393006A6813 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1464F9E727544393006A6813 /* Preview Assets.xcassets */; }; 15 | 1464FA0F2758ABB2006A6813 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1464FA0E2758ABB2006A6813 /* HeaderView.swift */; }; 16 | 1478D409278E2820004DF7BC /* MachineInitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1478D408278E2820004DF7BC /* MachineInitView.swift */; }; 17 | 14912A86275E9C2400ACF55D /* Machine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14912A85275E9C2400ACF55D /* Machine.swift */; }; 18 | 14912A88275EBFE200ACF55D /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14912A87275EBFE200ACF55D /* Shell.swift */; }; 19 | 14912A8C2760F3F200ACF55D /* BodyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14912A8B2760F3F200ACF55D /* BodyView.swift */; }; 20 | 14A0A7442761ECAC0063F087 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A0A7432761ECAC0063F087 /* MenuBar.swift */; }; 21 | 14A0A7462761ED1D0063F087 /* MenuBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A0A7452761ED1D0063F087 /* MenuBarController.swift */; }; 22 | 14A0A748276250430063F087 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A0A747276250430063F087 /* SettingsView.swift */; }; 23 | 14A5DF0A2790A49C0051B9AE /* MachineSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A5DF092790A49C0051B9AE /* MachineSelectView.swift */; }; 24 | 14B5E91027A0484800D756F4 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14B5E90F27A0484800D756F4 /* Errors.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1425F9D427A7E3E200057FDD /* ViewRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewRouter.swift; sourceTree = ""; }; 29 | 1464F9DD27544392006A6813 /* Podman Desktop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Podman Desktop.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 1464F9E027544392006A6813 /* Podman_DesktopApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Podman_DesktopApp.swift; sourceTree = ""; }; 31 | 1464F9E227544392006A6813 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 32 | 1464F9E427544393006A6813 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 33 | 1464F9E727544393006A6813 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 34 | 1464F9E927544393006A6813 /* Podman_Desktop.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Podman_Desktop.entitlements; sourceTree = ""; }; 35 | 1464FA0E2758ABB2006A6813 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; 36 | 1478D408278E2820004DF7BC /* MachineInitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachineInitView.swift; sourceTree = ""; }; 37 | 14912A85275E9C2400ACF55D /* Machine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Machine.swift; sourceTree = ""; }; 38 | 14912A87275EBFE200ACF55D /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; 39 | 14912A8B2760F3F200ACF55D /* BodyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BodyView.swift; sourceTree = ""; }; 40 | 14A0A7432761ECAC0063F087 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = ""; }; 41 | 14A0A7452761ED1D0063F087 /* MenuBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarController.swift; sourceTree = ""; }; 42 | 14A0A747276250430063F087 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; 43 | 14A5DF092790A49C0051B9AE /* MachineSelectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MachineSelectView.swift; sourceTree = ""; }; 44 | 14B5E90F27A0484800D756F4 /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 1464F9DA27544392006A6813 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 1464F9D427544392006A6813 = { 59 | isa = PBXGroup; 60 | children = ( 61 | 1464F9DF27544392006A6813 /* Podman Desktop */, 62 | 1464F9DE27544392006A6813 /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | 1464F9DE27544392006A6813 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 1464F9DD27544392006A6813 /* Podman Desktop.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | 1464F9DF27544392006A6813 /* Podman Desktop */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 1464F9E027544392006A6813 /* Podman_DesktopApp.swift */, 78 | 14912A83275E630000ACF55D /* Views */, 79 | 14912A84275E632500ACF55D /* Models */, 80 | 1464F9E427544393006A6813 /* Assets.xcassets */, 81 | 1464F9E927544393006A6813 /* Podman_Desktop.entitlements */, 82 | 1464F9E627544393006A6813 /* Preview Content */, 83 | ); 84 | path = "Podman Desktop"; 85 | sourceTree = ""; 86 | }; 87 | 1464F9E627544393006A6813 /* Preview Content */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 1464F9E727544393006A6813 /* Preview Assets.xcassets */, 91 | ); 92 | path = "Preview Content"; 93 | sourceTree = ""; 94 | }; 95 | 14912A83275E630000ACF55D /* Views */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 1464F9E227544392006A6813 /* ContentView.swift */, 99 | 14A0A747276250430063F087 /* SettingsView.swift */, 100 | 1478D408278E2820004DF7BC /* MachineInitView.swift */, 101 | 14A5DF092790A49C0051B9AE /* MachineSelectView.swift */, 102 | 1464FA0E2758ABB2006A6813 /* HeaderView.swift */, 103 | 14912A8B2760F3F200ACF55D /* BodyView.swift */, 104 | 14A0A7432761ECAC0063F087 /* MenuBar.swift */, 105 | ); 106 | path = Views; 107 | sourceTree = ""; 108 | }; 109 | 14912A84275E632500ACF55D /* Models */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 14912A85275E9C2400ACF55D /* Machine.swift */, 113 | 1425F9D427A7E3E200057FDD /* ViewRouter.swift */, 114 | 14912A87275EBFE200ACF55D /* Shell.swift */, 115 | 14B5E90F27A0484800D756F4 /* Errors.swift */, 116 | 14A0A7452761ED1D0063F087 /* MenuBarController.swift */, 117 | ); 118 | path = Models; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | 1464F9DC27544392006A6813 /* Podman Desktop */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = 1464F9EC27544393006A6813 /* Build configuration list for PBXNativeTarget "Podman Desktop" */; 127 | buildPhases = ( 128 | 1464F9D927544392006A6813 /* Sources */, 129 | 1464F9DA27544392006A6813 /* Frameworks */, 130 | 1464F9DB27544392006A6813 /* Resources */, 131 | ); 132 | buildRules = ( 133 | ); 134 | dependencies = ( 135 | ); 136 | name = "Podman"; 137 | productName = "Podman Desktop"; 138 | productReference = 1464F9DD27544392006A6813 /* Podman Desktop.app */; 139 | productType = "com.apple.product-type.application"; 140 | }; 141 | /* End PBXNativeTarget section */ 142 | 143 | /* Begin PBXProject section */ 144 | 1464F9D527544392006A6813 /* Project object */ = { 145 | isa = PBXProject; 146 | attributes = { 147 | BuildIndependentTargetsInParallel = 1; 148 | LastSwiftUpdateCheck = 1310; 149 | LastUpgradeCheck = 1310; 150 | TargetAttributes = { 151 | 1464F9DC27544392006A6813 = { 152 | CreatedOnToolsVersion = 13.1; 153 | }; 154 | }; 155 | }; 156 | buildConfigurationList = 1464F9D827544392006A6813 /* Build configuration list for PBXProject "Podman Desktop" */; 157 | compatibilityVersion = "Xcode 13.0"; 158 | developmentRegion = en; 159 | hasScannedForEncodings = 0; 160 | knownRegions = ( 161 | en, 162 | Base, 163 | ); 164 | mainGroup = 1464F9D427544392006A6813; 165 | productRefGroup = 1464F9DE27544392006A6813 /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | 1464F9DC27544392006A6813 /* Podman Desktop */, 170 | ); 171 | }; 172 | /* End PBXProject section */ 173 | 174 | /* Begin PBXResourcesBuildPhase section */ 175 | 1464F9DB27544392006A6813 /* Resources */ = { 176 | isa = PBXResourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | 1464F9E827544393006A6813 /* Preview Assets.xcassets in Resources */, 180 | 1464F9E527544393006A6813 /* Assets.xcassets in Resources */, 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | }; 184 | /* End PBXResourcesBuildPhase section */ 185 | 186 | /* Begin PBXSourcesBuildPhase section */ 187 | 1464F9D927544392006A6813 /* Sources */ = { 188 | isa = PBXSourcesBuildPhase; 189 | buildActionMask = 2147483647; 190 | files = ( 191 | 14A0A7462761ED1D0063F087 /* MenuBarController.swift in Sources */, 192 | 1425F9D527A7E3E200057FDD /* ViewRouter.swift in Sources */, 193 | 14912A8C2760F3F200ACF55D /* BodyView.swift in Sources */, 194 | 1478D409278E2820004DF7BC /* MachineInitView.swift in Sources */, 195 | 14A0A7442761ECAC0063F087 /* MenuBar.swift in Sources */, 196 | 14B5E91027A0484800D756F4 /* Errors.swift in Sources */, 197 | 1464F9E327544392006A6813 /* ContentView.swift in Sources */, 198 | 14A0A748276250430063F087 /* SettingsView.swift in Sources */, 199 | 14912A86275E9C2400ACF55D /* Machine.swift in Sources */, 200 | 14A5DF0A2790A49C0051B9AE /* MachineSelectView.swift in Sources */, 201 | 14912A88275EBFE200ACF55D /* Shell.swift in Sources */, 202 | 1464FA0F2758ABB2006A6813 /* HeaderView.swift in Sources */, 203 | 1464F9E127544392006A6813 /* Podman_DesktopApp.swift in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 1464F9EA27544393006A6813 /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_ENABLE_OBJC_WEAK = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = dwarf; 245 | ENABLE_STRICT_OBJC_MSGSEND = YES; 246 | ENABLE_TESTABILITY = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu11; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_NO_COMMON_BLOCKS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | ); 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | MACOSX_DEPLOYMENT_TARGET = 12.0; 262 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 263 | MTL_FAST_MATH = YES; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = macosx; 266 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 267 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 268 | }; 269 | name = Debug; 270 | }; 271 | 1464F9EB27544393006A6813 /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_ANALYZER_NONNULL = YES; 276 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_ENABLE_OBJC_WEAK = YES; 282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_COMMA = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INFINITE_RECURSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | COPY_PHASE_STRIP = NO; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | ENABLE_NS_ASSERTIONS = NO; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | GCC_C_LANGUAGE_STANDARD = gnu11; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | MACOSX_DEPLOYMENT_TARGET = 12.0; 317 | MTL_ENABLE_DEBUG_INFO = NO; 318 | MTL_FAST_MATH = YES; 319 | SDKROOT = macosx; 320 | SWIFT_COMPILATION_MODE = wholemodule; 321 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 322 | }; 323 | name = Release; 324 | }; 325 | 1464F9ED27544393006A6813 /* Debug */ = { 326 | isa = XCBuildConfiguration; 327 | buildSettings = { 328 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 329 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 330 | CODE_SIGN_ENTITLEMENTS = "Podman Desktop/Podman_Desktop.entitlements"; 331 | CODE_SIGN_STYLE = Automatic; 332 | COMBINE_HIDPI_IMAGES = YES; 333 | CURRENT_PROJECT_VERSION = 1; 334 | DEVELOPMENT_ASSET_PATHS = "\"Podman Desktop/Preview Content\""; 335 | ENABLE_PREVIEWS = YES; 336 | GENERATE_INFOPLIST_FILE = YES; 337 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 338 | LD_RUNPATH_SEARCH_PATHS = ( 339 | "$(inherited)", 340 | "@executable_path/../Frameworks", 341 | ); 342 | MARKETING_VERSION = 1.0; 343 | PRODUCT_BUNDLE_IDENTIFIER = "containers.Podman-Desktop"; 344 | PRODUCT_NAME = "$(TARGET_NAME)"; 345 | SWIFT_EMIT_LOC_STRINGS = YES; 346 | SWIFT_VERSION = 5.0; 347 | }; 348 | name = Debug; 349 | }; 350 | 1464F9EE27544393006A6813 /* Release */ = { 351 | isa = XCBuildConfiguration; 352 | buildSettings = { 353 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 354 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 355 | CODE_SIGN_ENTITLEMENTS = "Podman Desktop/Podman_Desktop.entitlements"; 356 | CODE_SIGN_STYLE = Automatic; 357 | COMBINE_HIDPI_IMAGES = YES; 358 | CURRENT_PROJECT_VERSION = 1; 359 | DEVELOPMENT_ASSET_PATHS = "\"Podman Desktop/Preview Content\""; 360 | ENABLE_PREVIEWS = YES; 361 | GENERATE_INFOPLIST_FILE = YES; 362 | INFOPLIST_KEY_NSHumanReadableCopyright = ""; 363 | LD_RUNPATH_SEARCH_PATHS = ( 364 | "$(inherited)", 365 | "@executable_path/../Frameworks", 366 | ); 367 | MARKETING_VERSION = 1.0; 368 | PRODUCT_BUNDLE_IDENTIFIER = "containers.Podman-Desktop"; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | SWIFT_EMIT_LOC_STRINGS = YES; 371 | SWIFT_VERSION = 5.0; 372 | }; 373 | name = Release; 374 | }; 375 | /* End XCBuildConfiguration section */ 376 | 377 | /* Begin XCConfigurationList section */ 378 | 1464F9D827544392006A6813 /* Build configuration list for PBXProject "Podman Desktop" */ = { 379 | isa = XCConfigurationList; 380 | buildConfigurations = ( 381 | 1464F9EA27544393006A6813 /* Debug */, 382 | 1464F9EB27544393006A6813 /* Release */, 383 | ); 384 | defaultConfigurationIsVisible = 0; 385 | defaultConfigurationName = Release; 386 | }; 387 | 1464F9EC27544393006A6813 /* Build configuration list for PBXNativeTarget "Podman Desktop" */ = { 388 | isa = XCConfigurationList; 389 | buildConfigurations = ( 390 | 1464F9ED27544393006A6813 /* Debug */, 391 | 1464F9EE27544393006A6813 /* Release */, 392 | ); 393 | defaultConfigurationIsVisible = 0; 394 | defaultConfigurationName = Release; 395 | }; 396 | /* End XCConfigurationList section */ 397 | }; 398 | rootObject = 1464F9D527544392006A6813 /* Project object */; 399 | } 400 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "scale" : "1x", 6 | "size" : "16x16" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "scale" : "2x", 11 | "size" : "16x16" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "scale" : "1x", 16 | "size" : "32x32" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "scale" : "2x", 21 | "size" : "32x32" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "scale" : "1x", 26 | "size" : "128x128" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "scale" : "2x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "scale" : "1x", 36 | "size" : "256x256" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "scale" : "2x", 41 | "size" : "256x256" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "scale" : "1x", 46 | "size" : "512x512" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "scale" : "2x", 51 | "size" : "512x512" 52 | } 53 | ], 54 | "info" : { 55 | "author" : "xcode", 56 | "version" : 1 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/debug.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "debug.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/debug.imageset/debug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "layer1-2.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-1.imageset/layer1-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : " podman.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-logo-sm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "podman-logo-sm.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-logo-sm.imageset/podman-logo-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/containers/podman-desktop-swift/3207107e6cc18370cde39593fbb43205bbbd6bf8/Podman Desktop/Assets.xcassets/podman-logo-sm.imageset/podman-logo-sm.png -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "podman-logo.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-logo.imageset/podman-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 21 | 26 | 27 | 33 | 38 | 39 | 45 | 50 | 51 | 54 | 61 | 62 | 63 | 65 | 66 | 68 | image/svg+xml 69 | 71 | 72 | 73 | 74 | 75 | 78 | 80 | 83 | 87 | 91 | 95 | 99 | 103 | 107 | 111 | 112 | 113 | 116 | 119 | 123 | 127 | 131 | 135 | 142 | 149 | 153 | 157 | 161 | 165 | 169 | 170 | 174 | 178 | 182 | 186 | 190 | 191 | 198 | 206 | 210 | 214 | 218 | 222 | 226 | 227 | 228 | 232 | 239 | 246 | 250 | 257 | 258 | 262 | 267 | 271 | 278 | 285 | 289 | 293 | 297 | 301 | 305 | 306 | 310 | 314 | 318 | 322 | 326 | 327 | 334 | 342 | 346 | 350 | 354 | 358 | 362 | 363 | 364 | 368 | 375 | 382 | 386 | 393 | 394 | 398 | 402 | 406 | 409 | 413 | 420 | 427 | 431 | 435 | 439 | 440 | 444 | 448 | 452 | 453 | 460 | 468 | 472 | 476 | 480 | 484 | 488 | 489 | 490 | 494 | 501 | 508 | 512 | 519 | 520 | 524 | 528 | 529 | 530 | 531 | 532 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/podman-purple.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.626", 9 | "green" : "0.174", 10 | "red" : "0.536" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.626", 27 | "green" : "0.174", 28 | "red" : "0.536" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/reboot.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "reboot.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/reboot.imageset/reboot.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/selkie-artwork.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "selkie-artwork.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/toggle-off.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.710", 9 | "green" : "0.699", 10 | "red" : "0.695" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Podman Desktop/Assets.xcassets/toggle-on.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.801", 9 | "green" : "0.602", 10 | "red" : "0.215" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Podman Desktop/Models/Errors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 1/25/22. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | enum MachineStartError: Error { 12 | case machineNotExist 13 | case alreadyRunning 14 | case genericError 15 | } 16 | 17 | 18 | enum MachineInitError: Error { 19 | case invalidCPUs 20 | case invalidIgnition 21 | case invalidImage 22 | case invalidDisk 23 | case invalidMemory 24 | case machineExists 25 | case genericError 26 | } 27 | 28 | enum MachineStop: Error { 29 | case genericError 30 | } 31 | 32 | func parseMachineStart(message: String) -> Error{ 33 | switch message { 34 | case let str where str.contains("VM does not exist"): 35 | return MachineStartError.machineNotExist 36 | case let str where str.contains("VM already running"): 37 | return MachineStartError.alreadyRunning 38 | default: 39 | return MachineStartError.genericError 40 | } 41 | } 42 | 43 | func parseMachineInitError(message: String) -> Error{ 44 | switch message { 45 | case let str where str.contains("VM already existst"): 46 | return MachineInitError.machineExists 47 | case let str where str.contains("cpus"): 48 | return MachineInitError.invalidCPUs 49 | case let str where str.contains("disk-size"): 50 | return MachineInitError.invalidDisk 51 | case let str where str.contains("ignition-path"): 52 | return MachineInitError.invalidIgnition 53 | case let str where str.contains("image-path"): 54 | return MachineInitError.invalidImage 55 | case let str where str.contains("memory"): 56 | return MachineInitError.invalidMemory 57 | default: 58 | return MachineStartError.genericError 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Podman Desktop/Models/Machine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 11/17/21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct Machine: Hashable, Codable { 12 | var name: String 13 | var dflt: Bool 14 | var created: String 15 | var running: Bool 16 | var lastup: String 17 | var stream: String 18 | var vmtype: String 19 | var cpus: Int 20 | var memory: String 21 | var disksize: String 22 | var port: Int 23 | var remoteUsername: String 24 | var identityPath: String 25 | 26 | private enum CodingKeys: String, CodingKey { 27 | case name = "Name" 28 | case dflt = "Default" 29 | case created = "Created" 30 | case running = "Running" 31 | case lastup = "LastUp" 32 | case stream = "Stream" 33 | case vmtype = "VMType" 34 | case cpus = "CPUs" 35 | case memory = "Memory" 36 | case disksize = "DiskSize" 37 | case port = "Port" 38 | case remoteUsername = "RemoteUsername" 39 | case identityPath = "IdentityPath" 40 | 41 | } 42 | } 43 | 44 | class AllMachines: ObservableObject{ 45 | @Published var lst: [Machine] 46 | @Published var activeMachine: Machine? 47 | @Published var defaultConnection: Bool 48 | @Published var running: Bool 49 | @Published var runningString: String 50 | @Published var noMachines: Bool 51 | // TODO: check if warn if active machine is not default connection 52 | // TODO: check if no machines exist 53 | 54 | /// Set to `listMachinesFromPodman` on real runs, only exists to support unit tests and previews. 55 | private let machineListRetriever: () throws -> [Machine] 56 | 57 | /// Create a new observer of the Podman machine set. 58 | /// - Parameter machineListRetriever: A non-default provider of the machine state, intended only to support unit tests and previews. 59 | init(machineListRetriever: @escaping () throws -> [Machine] = listMachinesFromPodman) { 60 | self.machineListRetriever = machineListRetriever 61 | self.lst=[] 62 | activeMachine = nil 63 | defaultConnection = false 64 | running = false 65 | runningString = "not running" 66 | noMachines = false 67 | } 68 | 69 | private static func listMachinesFromPodman() throws -> [Machine] { 70 | let output = try machineList() 71 | let jsonData = output.1.data(using: .utf8)! 72 | return try! JSONDecoder().decode([Machine].self, from: jsonData) 73 | } 74 | 75 | func loadLst(){ 76 | var jsons = [Machine]() 77 | do { 78 | jsons = try self.machineListRetriever() 79 | } 80 | catch { 81 | print("\(error)") // TODO: plumb custom errors 82 | } 83 | self.lst=jsons 84 | } 85 | func getRunning() -> Machine?{ 86 | for mach in self.lst{ 87 | if mach.running{ 88 | return mach 89 | } 90 | } 91 | return nil 92 | } 93 | 94 | func isRunning() -> Bool?{ 95 | return activeMachine?.running 96 | } 97 | 98 | func getMachine(name: String) -> Machine?{ 99 | for mach in self.lst{ 100 | if mach.name == name{ 101 | return mach 102 | } 103 | } 104 | return nil 105 | } 106 | 107 | func getCLIDefault() -> Machine?{ 108 | for mach in self.lst{ 109 | if mach.dflt{ 110 | return(mach) 111 | } 112 | } 113 | return(nil) 114 | } 115 | 116 | func reloadAll(){ 117 | loadLst() 118 | loadActiveMachine() 119 | checkDefaultConnection() 120 | loadRunning() 121 | 122 | } 123 | 124 | func loadRunning() { 125 | running = activeMachine?.running ?? false // Should we have an extra state to represent “N/A, no machine exists”? 126 | runningString = running ? "running" : "not running" 127 | } 128 | func loadActiveMachine() { 129 | // 1. Active machine is running 130 | 131 | // No machines are running, so no active machine. 132 | if lst.count == 0{ 133 | activeMachine = nil 134 | return 135 | } 136 | // If a machine is running, it is ALWAYS the active machine 137 | let runningMachine = getRunning() 138 | if runningMachine != nil{ 139 | setActiveMachine(machine: runningMachine!) 140 | return 141 | } 142 | 143 | // Check which machine was active last, that is the active machine 144 | let lastRunName = UserDefaults.standard.string(forKey: "activeName") 145 | if lastRunName != nil{ 146 | // Check if cached machine name actually still exists 147 | let maybeMachine = getMachine(name: lastRunName!) 148 | if maybeMachine == nil{ 149 | // Cached last active no longer exists, clear 150 | UserDefaults.standard.set("", forKey: "activeName") 151 | } else { 152 | setActiveMachine(machine: maybeMachine!) 153 | return 154 | } 155 | } 156 | // CLI default machine is the active machine 157 | let cliDef = getCLIDefault() 158 | if cliDef != nil { 159 | setActiveMachine(machine: cliDef!) 160 | return 161 | } 162 | // No previously active machine, just choose the first machine on the list 163 | setActiveMachine(machine: lst[0]) 164 | return 165 | } 166 | 167 | func setActiveMachine(machine: Machine){ 168 | activeMachine = machine 169 | UserDefaults.standard.set(machine.name, forKey: "activeName") 170 | } 171 | 172 | func changeActiveMachine(machine: Machine){ 173 | activeMachine = machine 174 | UserDefaults.standard.set(activeMachine!.name, forKey: "activeName") 175 | } 176 | 177 | func checkDefaultConnection(){ 178 | if activeMachine == nil{ 179 | defaultConnection = false 180 | return 181 | } 182 | defaultConnection = activeMachine!.dflt 183 | } 184 | 185 | func startActive() async throws -> Int32{ 186 | reloadAll() 187 | let output = try shell(arguments: ["podman","machine", "start", activeMachine!.name]) 188 | return output.0 189 | } 190 | 191 | func stopActive() async throws -> Int32{ 192 | reloadAll() 193 | let output = try shell(arguments: ["podman","machine", "stop", activeMachine!.name]) 194 | return output.0 195 | } 196 | 197 | } 198 | 199 | 200 | class NewMachineInit: ObservableObject { 201 | @Published var name: String 202 | @Published var ignitionPath: String 203 | @Published var imagePath: String 204 | @Published var cpus: Int 205 | @Published var memory: Int 206 | @Published var disksize: Int 207 | 208 | 209 | init(){ 210 | self.name = "new_machine" 211 | self.ignitionPath="" 212 | self.imagePath="next" 213 | self.cpus=1 214 | self.memory=2040 215 | self.disksize=10 216 | } 217 | func validate(){ 218 | //TODO: validate 219 | 220 | } 221 | func create() throws { 222 | do { 223 | try machineInit(mach:self) 224 | 225 | } 226 | catch { 227 | print("\(error)") // TODO: plumb custom errors 228 | } 229 | } 230 | } 231 | 232 | // Fake AllMachines data only to make it easy to show realistic previews. 233 | extension AllMachines { 234 | static private func fakeMachine(name: String, isRunning: Bool = false, isDefault: Bool = false) -> Machine { 235 | // Use more realistic data? 236 | Machine(name: name, dflt: isDefault, created: "TODO", running: isRunning, lastup: "TODO", stream: "TODO", vmtype: "TODO", cpus: 1, memory: "TODO", disksize: "TODO", port: 8888, remoteUsername: "TODO", identityPath: "TODO") 237 | } 238 | 239 | static private func previewWithData(_ data: [Machine]) -> AllMachines { 240 | let res = AllMachines(machineListRetriever: { return data }) 241 | // Currently, creating an AllMachines() does not trigger actually loading the data. 242 | // In real runs, that is only triggered when the top-level ContentView appears; 243 | // for previews of individual views, we need to trigger the refresh explicitly. 244 | res.reloadAll() 245 | return res 246 | } 247 | 248 | static func previewWithNoMachines() -> AllMachines { 249 | return previewWithData([Machine]()) 250 | } 251 | 252 | static func previewWithOneRunningMachine() -> AllMachines { 253 | return previewWithData( 254 | [fakeMachine(name: "fedora", isRunning: true)] 255 | ) 256 | } 257 | 258 | static func previewWithOneStoppedMachine() -> AllMachines { 259 | return previewWithData( 260 | [fakeMachine(name: "fedora", isRunning: false)] 261 | ) 262 | } 263 | 264 | static func previewWithSeveralMachines() -> AllMachines { 265 | return previewWithData( 266 | [ 267 | fakeMachine(name: "ubuntu"), 268 | fakeMachine(name: "fedora-running", isRunning: true), 269 | fakeMachine(name: "centos-default", isDefault: true), 270 | fakeMachine(name: "rhel"), 271 | ] 272 | ) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /Podman Desktop/Models/MenuBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuBarController.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 12/9/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | class StatusBarController { 11 | private var statusBar: NSStatusBar 12 | private var statusItem: NSStatusItem 13 | private var popover: NSPopover 14 | 15 | init(_ popover: NSPopover) { 16 | self.popover = popover 17 | statusBar = NSStatusBar.init() 18 | // Creating a status bar item having a fixed length 19 | statusItem = statusBar.statusItem(withLength: 30.0) 20 | 21 | if let statusBarButton = statusItem.button { 22 | statusBarButton.image = #imageLiteral(resourceName: "podman-logo-sm") 23 | statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0) 24 | statusBarButton.image?.isTemplate = true 25 | 26 | statusBarButton.action = #selector(togglePopover(sender:)) 27 | statusBarButton.target = self 28 | } 29 | } 30 | 31 | @objc func togglePopover(sender: AnyObject) { 32 | if(popover.isShown) { 33 | hidePopover(sender) 34 | } 35 | else { 36 | showPopover(sender) 37 | } 38 | } 39 | 40 | func showPopover(_ sender: AnyObject) { 41 | if let statusBarButton = statusItem.button { 42 | popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY) 43 | } 44 | } 45 | 46 | func hidePopover(_ sender: AnyObject) { 47 | popover.performClose(sender) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Podman Desktop/Models/Shell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelData.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 11/24/21. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | func shell(arguments: [String]) throws -> (Int32, String){ 12 | var str="" 13 | let process = Process() 14 | let pipe = Pipe() 15 | process.executableURL = URL(fileURLWithPath: "/usr/bin/env") 16 | process.arguments = arguments 17 | process.standardOutput = pipe 18 | process.standardError = pipe 19 | var environment = ProcessInfo.processInfo.environment 20 | 21 | environment["PATH"] = (environment["PATH"] ?? "")+":/opt/homebrew/bin/:/usr/local/bin/" 22 | process.environment = environment 23 | do { 24 | try process.run() 25 | } 26 | catch{ 27 | throw error } 28 | 29 | let outHandle = pipe.fileHandleForReading 30 | // TODO: manage output readability better here 31 | outHandle.readabilityHandler = { pipe in 32 | if let line = String(data: pipe.availableData, encoding: String.Encoding.utf8) { 33 | str+=line 34 | } 35 | } 36 | 37 | 38 | process.waitUntilExit() 39 | let status = process.terminationStatus 40 | // remove the trailing new-line char 41 | return (status, str) 42 | } 43 | 44 | func machineList() throws -> (Int32, String){ 45 | return try shell(arguments: ["podman","machine", "list", "--format", "json"]) 46 | } 47 | 48 | func machineInit(mach: NewMachineInit) throws{ 49 | try shell(arguments: ["podman","machine", "init", "--cpus", String(mach.cpus), "--memory", String(mach.memory), "--disk-size", String(mach.disksize), "--ignition-path", mach.ignitionPath, "--image-path", mach.imagePath, mach.name]) 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Podman Desktop/Models/ViewRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewRouter.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 1/28/22. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | class ViewRouter: ObservableObject { 12 | 13 | @Published var currentPage: Page = .land 14 | 15 | } 16 | 17 | enum Page { 18 | case land 19 | case settings 20 | case machineSelect 21 | case machineInit 22 | } 23 | -------------------------------------------------------------------------------- /Podman Desktop/Podman_Desktop.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Podman Desktop/Podman_DesktopApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Podman_DesktopApp.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 11/9/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct Podman_DesktopApp: App { 12 | @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate 13 | init() { 14 | } 15 | 16 | var body: some Scene { 17 | Settings { 18 | EmptyView() 19 | } 20 | WindowGroup("Viewer") { // other scene 21 | // TestView() 22 | ContentView() 23 | // SettingsView() 24 | } 25 | .windowStyle(HiddenTitleBarWindowStyle()) 26 | } 27 | } 28 | 29 | class AppDelegate: NSObject, NSApplicationDelegate { 30 | var popover = NSPopover.init() 31 | var statusBar: StatusBarController? 32 | 33 | func applicationDidFinishLaunching(_ aNotification: Notification) { 34 | // if let window = NSApplication.shared.windows.first { 35 | // window.close() 36 | // } 37 | // Create the SwiftUI view that provides the contents 38 | let contentView = MenuBar() 39 | 40 | // Set the SwiftUI's ContentView to the Popover's ContentViewController 41 | popover.contentSize = NSSize(width: 220, height: 200) 42 | popover.contentViewController = NSHostingController(rootView: contentView) 43 | 44 | // Create the Status Bar Item with the above Popover 45 | statusBar = StatusBarController.init(popover) 46 | // Timer.scheduledTimer(withTimeInterval: 3, repeats: true, block: { _ in 47 | // if self.window.isVisible { 48 | // NSApp.hide(self) 49 | // } else { 50 | // NSApp.unhide(self) 51 | // } 52 | // } 53 | 54 | func applicationWillTerminate(_ aNotification: Notification) { 55 | //teardown 56 | } 57 | 58 | } 59 | // var window: NSWindow! 60 | // var preferencesWindow: NSWindow! 61 | // 62 | // @objc func openPreferencesWindow() { 63 | // if nil == preferencesWindow { 64 | // let preferencesView = ContentView() 65 | // preferencesWindow = NSWindow( 66 | // contentRect: NSRect(x: 20, y: 20, width: 480, height: 300), 67 | // styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], 68 | // backing: .buffered, 69 | // defer: false) 70 | // preferencesWindow.center() 71 | // preferencesWindow.setFrameAutosaveName("Preferences") 72 | // preferencesWindow.isReleasedWhenClosed = false 73 | // preferencesWindow.contentView = NSHostingView(rootView: preferencesView) 74 | // } 75 | // preferencesWindow.makeKeyAndOrderFront(nil) 76 | // } 77 | } 78 | -------------------------------------------------------------------------------- /Podman Desktop/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Podman Desktop/Views/BodyView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Body.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 11/10/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct BodyView: View { 11 | @EnvironmentObject var allMachines: AllMachines 12 | var body: some View { 13 | VStack{ 14 | Spacer() 15 | Image("selkie-artwork") 16 | .resizable() 17 | .scaledToFit() 18 | .frame(width: 150) 19 | Text("Welcome to Podman Desktop") 20 | .font(.title) 21 | .padding() 22 | Text("To start working with containers on Podman, \nyou'll need to run the Podman service.") 23 | .multilineTextAlignment(.center) 24 | Button { 25 | allMachines.reloadAll() 26 | if !allMachines.running{ 27 | Task{ 28 | do { 29 | try await allMachines.startActive() 30 | allMachines.reloadAll() 31 | 32 | } 33 | catch {print("error")} // TODO: plumb custom errors 34 | } 35 | 36 | } else { 37 | Task{ 38 | do {try await allMachines.stopActive() 39 | allMachines.reloadAll() 40 | } 41 | catch {print("error")} // TODO: plumb custom errors 42 | } 43 | } 44 | } label: { 45 | Image(systemName: "power.circle.fill") 46 | .resizable() 47 | .scaledToFit() 48 | .foregroundStyle(allMachines.running ? .green : .white, allMachines.running ? .white : .purple ) 49 | .frame(width: 50, height: 50) 50 | }.padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0)) 51 | .buttonStyle(PlainButtonStyle()) 52 | Spacer() 53 | } 54 | } 55 | } 56 | 57 | struct BodyView_Previews: PreviewProvider { 58 | static var previews: some View { 59 | let situations: [(name: String, allMachines: AllMachines)] = [ 60 | ("Running", AllMachines.previewWithOneRunningMachine()), 61 | ("Stopped", AllMachines.previewWithOneStoppedMachine()), 62 | ("No machine", AllMachines.previewWithNoMachines()), 63 | ] 64 | ForEach(situations, id: \.name) { s in 65 | BodyView() 66 | .environmentObject(s.allMachines) 67 | .previewDisplayName(s.name) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Podman Desktop/Views/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 11/9/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | 11 | 12 | struct ContentView: View { 13 | var allMachines = AllMachines() 14 | @StateObject var viewRouter = ViewRouter() 15 | var body: some View { 16 | VStack(spacing: 0){ 17 | HeaderView() 18 | .frame(height: 70) 19 | .environmentObject(allMachines) 20 | .environmentObject(viewRouter) 21 | .onAppear { 22 | allMachines.reloadAll() 23 | } 24 | 25 | switch viewRouter.currentPage { 26 | case .land: 27 | BodyView() 28 | .environmentObject(viewRouter) 29 | .environmentObject(allMachines) 30 | case .settings: 31 | SettingsView() 32 | .environmentObject(viewRouter) 33 | .environmentObject(allMachines) 34 | case .machineSelect: 35 | MachineSelectView() 36 | .environmentObject(viewRouter) 37 | .environmentObject(allMachines) 38 | case .machineInit: 39 | MachineInitView() 40 | .environmentObject(viewRouter) 41 | .environmentObject(allMachines) 42 | } 43 | Spacer() 44 | } 45 | .ignoresSafeArea() 46 | .frame(minWidth: 800, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity) 47 | } 48 | } 49 | 50 | 51 | struct ContentView_Previews: PreviewProvider { 52 | static var previews: some View { 53 | ContentView() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Podman Desktop/Views/HeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Header.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 12/2/21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct HeaderView: View { 11 | @EnvironmentObject var viewRouter: ViewRouter 12 | var body: some View { 13 | ZStack { 14 | Color("podman-purple") 15 | .ignoresSafeArea() 16 | 17 | ZStack{ 18 | Image("podman-1") 19 | .resizable() 20 | .scaledToFit() 21 | .frame(width: 200) 22 | MachineControls() 23 | } 24 | } 25 | } 26 | } 27 | // 28 | struct MachineControls: View{ 29 | 30 | @EnvironmentObject var viewRouter: ViewRouter 31 | @EnvironmentObject var allMachines: AllMachines 32 | @State var starting: Bool = false 33 | var body: some View{ 34 | 35 | 36 | HStack{ 37 | Spacer() 38 | Text("Podman is:") 39 | .foregroundColor(.gray) 40 | if starting{ 41 | Text("starting") 42 | .foregroundColor(.white) 43 | ProgressView() 44 | } else{ 45 | Text (allMachines.runningString) 46 | .foregroundColor(.white) 47 | } 48 | Button { 49 | allMachines.reloadAll() 50 | if !allMachines.running{ 51 | Task{ 52 | do {starting = true 53 | var exitcode = try await allMachines.startActive() 54 | allMachines.reloadAll() 55 | starting = false 56 | } 57 | catch {print("error")} // TODO: plumb custom errors 58 | } 59 | 60 | } else { 61 | Task{ 62 | do {try await allMachines.stopActive() 63 | allMachines.reloadAll() 64 | } 65 | catch {print("error")} // TODO: plumb custom errors 66 | } 67 | } 68 | } label: { 69 | Image(systemName: "power.circle.fill") 70 | .resizable() 71 | .scaledToFit() 72 | .foregroundStyle(allMachines.running ? .green : .white, allMachines.running ? .white : .purple ) 73 | .frame(width: 25, height: 25) 74 | } 75 | .padding(.trailing) 76 | 77 | Button(action: { 78 | if viewRouter.currentPage == .land{ 79 | viewRouter.currentPage = .settings 80 | }else{ 81 | viewRouter.currentPage = .land 82 | } 83 | }){ 84 | Image(systemName: "gearshape") 85 | .resizable() 86 | .scaledToFit() 87 | .foregroundColor(.white) 88 | .frame(width: 20, height: 20) 89 | } 90 | .padding(.trailing) 91 | .opacity(1) 92 | 93 | } 94 | .buttonStyle(PlainButtonStyle()) 95 | } 96 | } 97 | 98 | struct HeaderView_Previews: PreviewProvider { 99 | static var previews: some View { 100 | let situations: [(name: String, allMachines: AllMachines)] = [ 101 | ("Running", AllMachines.previewWithOneRunningMachine()), 102 | ("Stopped", AllMachines.previewWithOneStoppedMachine()), 103 | ("No machine", AllMachines.previewWithNoMachines()), 104 | ] 105 | ForEach(situations, id: \.name) { s in 106 | HeaderView() 107 | .environmentObject(s.allMachines) 108 | .environmentObject(ViewRouter()) 109 | .previewDisplayName(s.name) 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Podman Desktop/Views/MachineInitView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachineInitView.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 12/18/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MachineInitView: View { 11 | @EnvironmentObject var viewRouter: ViewRouter 12 | @EnvironmentObject var allMachines: AllMachines 13 | @ObservedObject var newMachine = NewMachineInit() 14 | var streams = ["stable", "testing", "next"] 15 | @State private var vmImg = "fcos" 16 | @State var customVM = false 17 | @State var customIgn = false 18 | @State private var ignFile = "" 19 | @State var imgFile = "" 20 | 21 | var memoryToInt: Binding{ 22 | Binding(get: { 23 | //returns the score as a Double 24 | return Double(newMachine.memory) 25 | }, set: { 26 | //rounds the double to an Int 27 | newMachine.memory = Int($0) 28 | }) 29 | } 30 | 31 | var diskToInt: Binding{ 32 | Binding(get: { 33 | //returns the score as a Double 34 | return Double(newMachine.disksize) 35 | }, set: { 36 | //rounds the double to an Int 37 | newMachine.disksize = Int($0) 38 | }) 39 | } 40 | 41 | var body: some View { 42 | Group{ 43 | ZStack{ 44 | Color.white 45 | HStack{ 46 | Text("Create a new VM") 47 | .multilineTextAlignment(.leading) 48 | .font(.title2) 49 | } 50 | .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)) 51 | } 52 | .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) 53 | .frame(height: 50, alignment: .leading) 54 | } 55 | Spacer() 56 | VStack{ 57 | HStack{ 58 | Text("Machine Name") 59 | .frame(width:100) 60 | TextField( 61 | "VM Name", 62 | text: $newMachine.name 63 | ) 64 | } 65 | 66 | HStack(alignment: .firstTextBaseline){ 67 | Text("VM OS") 68 | .frame(width:100) 69 | 70 | Picker("", selection: $vmImg) { // <2> 71 | HStack{ 72 | Text("Fedora CoreOS") // <3> 73 | Picker("",selection: $newMachine.imagePath) { 74 | ForEach(streams, id: \.self) { 75 | Text($0) 76 | } 77 | } 78 | .disabled(vmImg != "fcos") 79 | .pickerStyle(SegmentedPickerStyle()) 80 | }.tag("fcos") 81 | 82 | HStack{ 83 | Button("Custom Image"){ 84 | let panel = NSOpenPanel() 85 | panel.allowsMultipleSelection = false 86 | panel.canChooseDirectories = false 87 | if panel.runModal() == .OK { 88 | self.imgFile = panel.url?.path ?? "select" 89 | } 90 | } 91 | .disabled(vmImg != "custom") 92 | Text(imgFile) 93 | .frame(width: 250, height: 10) 94 | .truncationMode(.head) 95 | }.tag("custom") 96 | } 97 | .pickerStyle(RadioGroupPickerStyle()) 98 | } 99 | 100 | HStack{ 101 | Text("CPUs") 102 | .frame(width:100) 103 | TextField( 104 | "CPUs", 105 | value: $newMachine.cpus, 106 | format: .number 107 | ) 108 | .frame(width: 25) 109 | Stepper("",value: $newMachine.cpus) 110 | Spacer() 111 | } 112 | HStack{ 113 | Text("RAM") 114 | .frame(width:100) 115 | TextField( 116 | "Memory", 117 | value: $newMachine.memory, 118 | format: .number 119 | ) 120 | .frame(width: 65) 121 | 122 | Text("MB") 123 | Slider.ln(value: memoryToInt, in: 1 ... 65536) 124 | } 125 | 126 | HStack{ 127 | Text("Disk size") 128 | .frame(width:100) 129 | TextField( 130 | "Disk size", 131 | value: $newMachine.disksize, 132 | format: .number 133 | ) 134 | .frame(width: 65) 135 | Text("GB") 136 | Slider.ln(value: diskToInt, in: 16 ... 2048) 137 | } 138 | 139 | HStack{ 140 | Text("Ignition Path") 141 | .frame(width:100) 142 | Picker("", selection: $customIgn) { // <2> 143 | Text("Generate Default") // <3> 144 | .tag(false) 145 | HStack{ 146 | Button("Upload Custom Ignition"){ 147 | let panel = NSOpenPanel() 148 | panel.allowsMultipleSelection = false 149 | panel.canChooseDirectories = false 150 | if panel.runModal() == .OK { 151 | self.ignFile = panel.url?.path ?? "select" 152 | } 153 | } 154 | .disabled(!customIgn) 155 | Text(ignFile) 156 | .frame(width: 200, height: 10) 157 | .truncationMode(.head) 158 | }.tag(true) 159 | } 160 | .pickerStyle(RadioGroupPickerStyle()) 161 | } 162 | HStack{ 163 | Button("Cancel"){ 164 | viewRouter.currentPage = .machineSelect 165 | } 166 | Button("Submit") { 167 | // TODO: need to validate data, make sure no fields are emptyhere 168 | do{ 169 | try newMachine.create() 170 | allMachines.reloadAll() 171 | } catch { 172 | print("error") // TODO: plumb custom errors 173 | } 174 | viewRouter.currentPage = .machineSelect 175 | } 176 | } 177 | .padding(40) 178 | } 179 | .padding(EdgeInsets(top: 30, leading: 0, bottom: 0, trailing: 0)) 180 | .frame(width: 500) 181 | Spacer() 182 | } 183 | } 184 | 185 | 186 | struct MachineInitView_Previews: PreviewProvider { 187 | static var previews: some View { 188 | VStack { 189 | MachineInitView() 190 | } 191 | .environmentObject(AllMachines.previewWithOneRunningMachine()) 192 | .environmentObject(ViewRouter()) 193 | } 194 | } 195 | 196 | 197 | extension Binding where Value == Double { 198 | func logarithmic(base: Double = 2) -> Binding { 199 | Binding( 200 | get: { 201 | log2(self.wrappedValue) / log2(base) 202 | }, 203 | set: { (newValue) in 204 | self.wrappedValue = pow(base, newValue) 205 | } 206 | ) 207 | } 208 | } 209 | 210 | extension Slider where Label == EmptyView, ValueLabel == EmptyView { 211 | 212 | static func ln( 213 | value: Binding, 214 | in range: ClosedRange, 215 | onEditingChanged: @escaping (Bool) -> Void = { _ in } 216 | ) -> Slider { 217 | return self.init( 218 | value: value.logarithmic(), 219 | in: log2(range.lowerBound) ... log2(range.upperBound), 220 | step: 1, 221 | onEditingChanged: onEditingChanged 222 | ) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Podman Desktop/Views/MachineSelectView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MachineInitView.swift 3 | // Podman Desktop 4 | // 5 | // Created by Ashley Cui on 12/18/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MachineSelectView: View { 11 | @EnvironmentObject var viewRouter: ViewRouter 12 | @EnvironmentObject var allMachines: AllMachines 13 | @State private var chosenVM = "" 14 | 15 | @State private var machineIndex = 0 16 | 17 | var body: some View { 18 | Group{ 19 | Form { 20 | Picker("Select a machine", selection: $machineIndex) { 21 | ForEach(0..