diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj index 170b81a08..6056648be 100644 --- a/CodeEdit.xcodeproj/project.pbxproj +++ b/CodeEdit.xcodeproj/project.pbxproj @@ -316,7 +316,7 @@ 617DB3D02C25AFAE00B58BFE /* TaskNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */; }; 617DB3D32C25AFEA00B58BFE /* TaskNotificationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D22C25AFEA00B58BFE /* TaskNotificationModel.swift */; }; 617DB3D62C25B02D00B58BFE /* TaskNotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */; }; - 617DB3D82C25B04D00B58BFE /* CustomLoadingRingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */; }; + 617DB3D82C25B04D00B58BFE /* CECircularProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D72C25B04D00B58BFE /* CECircularProgressView.swift */; }; 617DB3DA2C25B07F00B58BFE /* TaskNotificationsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */; }; 617DB3DC2C25B14A00B58BFE /* ActivityViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */; }; 617DB3DF2C25E13800B58BFE /* TaskNotificationHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 617DB3DE2C25E13800B58BFE /* TaskNotificationHandlerTests.swift */; }; @@ -325,7 +325,6 @@ 618725A62C29F02500987354 /* DropdownMenuItemStyleModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725A52C29F02500987354 /* DropdownMenuItemStyleModifier.swift */; }; 618725A82C29F05500987354 /* OptionMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725A72C29F05500987354 /* OptionMenuItemView.swift */; }; 618725AB2C29F2C000987354 /* TaskDropDownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725AA2C29F2C000987354 /* TaskDropDownView.swift */; }; - 618725B02C2DBBE900987354 /* TaskSidebarTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725AF2C2DBBE900987354 /* TaskSidebarTileView.swift */; }; 618725B22C2DBC0C00987354 /* TaskOutputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725B12C2DBC0C00987354 /* TaskOutputView.swift */; }; 618725B42C2DBC2800987354 /* TaskOutputActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 618725B32C2DBC2800987354 /* TaskOutputActionsView.swift */; }; 6195E30D2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6195E30C2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift */; }; @@ -528,9 +527,14 @@ B67DB0F62AFC2A7A002DC647 /* FindNavigatorToolbarBottom.swift in Sources */ = {isa = PBXBuildFile; fileRef = B67DB0F52AFC2A7A002DC647 /* FindNavigatorToolbarBottom.swift */; }; B67DB0F92AFDF638002DC647 /* IconButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B67DB0F82AFDF638002DC647 /* IconButtonStyle.swift */; }; B67DB0FC2AFDF71F002DC647 /* IconToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B67DB0FB2AFDF71F002DC647 /* IconToggleStyle.swift */; }; + B68108042C60287F008B27C1 /* StartTaskToolbarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B68108032C60287F008B27C1 /* StartTaskToolbarButton.swift */; }; B685DE7929CC9CCD002860C8 /* StatusBarIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = B685DE7829CC9CCD002860C8 /* StatusBarIcon.swift */; }; B697937A29FF5668002027EC /* AccountsSettingsAccountLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B697937929FF5668002027EC /* AccountsSettingsAccountLink.swift */; }; B69BFDC72B0686910050D9A6 /* GitClient+Initiate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69BFDC62B0686910050D9A6 /* GitClient+Initiate.swift */; }; + B69D3EDE2C5E85A2005CF43A /* StopTaskToolbarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69D3EDD2C5E85A2005CF43A /* StopTaskToolbarButton.swift */; }; + B69D3EE12C5F5357005CF43A /* TaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69D3EE02C5F5357005CF43A /* TaskView.swift */; }; + B69D3EE32C5F536B005CF43A /* ActiveTaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69D3EE22C5F536B005CF43A /* ActiveTaskView.swift */; }; + B69D3EE52C5F54B3005CF43A /* TasksPopoverMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69D3EE42C5F54B3005CF43A /* TasksPopoverMenuItem.swift */; }; B6A43C5D29FC4AF00027E0E0 /* CreateSSHKeyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6A43C5C29FC4AF00027E0E0 /* CreateSSHKeyView.swift */; }; B6AB09A12AAABAAE0003A3A6 /* EditorTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AB09A02AAABAAE0003A3A6 /* EditorTabs.swift */; }; B6AB09A32AAABFEC0003A3A6 /* EditorTabBarLeadingAccessories.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6AB09A22AAABFEC0003A3A6 /* EditorTabBarLeadingAccessories.swift */; }; @@ -952,7 +956,7 @@ 617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationHandler.swift; sourceTree = ""; }; 617DB3D22C25AFEA00B58BFE /* TaskNotificationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationModel.swift; sourceTree = ""; }; 617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationView.swift; sourceTree = ""; }; - 617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLoadingRingView.swift; sourceTree = ""; }; + 617DB3D72C25B04D00B58BFE /* CECircularProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CECircularProgressView.swift; sourceTree = ""; }; 617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationsDetailView.swift; sourceTree = ""; }; 617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityViewer.swift; sourceTree = ""; }; 617DB3DE2C25E13800B58BFE /* TaskNotificationHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskNotificationHandlerTests.swift; sourceTree = ""; }; @@ -961,7 +965,6 @@ 618725A52C29F02500987354 /* DropdownMenuItemStyleModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownMenuItemStyleModifier.swift; sourceTree = ""; }; 618725A72C29F05500987354 /* OptionMenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionMenuItemView.swift; sourceTree = ""; }; 618725AA2C29F2C000987354 /* TaskDropDownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskDropDownView.swift; sourceTree = ""; }; - 618725AF2C2DBBE900987354 /* TaskSidebarTileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskSidebarTileView.swift; sourceTree = ""; }; 618725B12C2DBC0C00987354 /* TaskOutputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskOutputView.swift; sourceTree = ""; }; 618725B32C2DBC2800987354 /* TaskOutputActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskOutputActionsView.swift; sourceTree = ""; }; 6195E30C2B64044F007261CA /* WorkspaceDocument+SearchState+FindTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WorkspaceDocument+SearchState+FindTests.swift"; sourceTree = ""; }; @@ -1158,9 +1161,14 @@ B67DB0F52AFC2A7A002DC647 /* FindNavigatorToolbarBottom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindNavigatorToolbarBottom.swift; sourceTree = ""; }; B67DB0F82AFDF638002DC647 /* IconButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconButtonStyle.swift; sourceTree = ""; }; B67DB0FB2AFDF71F002DC647 /* IconToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconToggleStyle.swift; sourceTree = ""; }; + B68108032C60287F008B27C1 /* StartTaskToolbarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartTaskToolbarButton.swift; sourceTree = ""; }; B685DE7829CC9CCD002860C8 /* StatusBarIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarIcon.swift; sourceTree = ""; }; B697937929FF5668002027EC /* AccountsSettingsAccountLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsSettingsAccountLink.swift; sourceTree = ""; }; B69BFDC62B0686910050D9A6 /* GitClient+Initiate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Initiate.swift"; sourceTree = ""; }; + B69D3EDD2C5E85A2005CF43A /* StopTaskToolbarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopTaskToolbarButton.swift; sourceTree = ""; }; + B69D3EE02C5F5357005CF43A /* TaskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskView.swift; sourceTree = ""; }; + B69D3EE22C5F536B005CF43A /* ActiveTaskView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveTaskView.swift; sourceTree = ""; }; + B69D3EE42C5F54B3005CF43A /* TasksPopoverMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TasksPopoverMenuItem.swift; sourceTree = ""; }; B6A43C5C29FC4AF00027E0E0 /* CreateSSHKeyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSSHKeyView.swift; sourceTree = ""; }; B6AB09A02AAABAAE0003A3A6 /* EditorTabs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorTabs.swift; sourceTree = ""; }; B6AB09A22AAABFEC0003A3A6 /* EditorTabBarLeadingAccessories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditorTabBarLeadingAccessories.swift; sourceTree = ""; }; @@ -2558,7 +2566,7 @@ children = ( 617DB3DB2C25B14A00B58BFE /* ActivityViewer.swift */, 618725A22C29EFE200987354 /* Tasks */, - 618725A92C29F09900987354 /* Notificaitons */, + 618725A92C29F09900987354 /* Notifications */, 617DB3D12C25AFD300B58BFE /* Models */, ); path = ActivityViewer; @@ -2585,6 +2593,9 @@ children = ( 618725A02C29EFCC00987354 /* SchemeDropDownView.swift */, 618725AA2C29F2C000987354 /* TaskDropDownView.swift */, + B69D3EE02C5F5357005CF43A /* TaskView.swift */, + B69D3EE22C5F536B005CF43A /* ActiveTaskView.swift */, + B69D3EE42C5F54B3005CF43A /* TasksPopoverMenuItem.swift */, 618725A32C29F00400987354 /* WorkspaceMenuItemView.swift */, 618725A72C29F05500987354 /* OptionMenuItemView.swift */, 618725A52C29F02500987354 /* DropdownMenuItemStyleModifier.swift */, @@ -2592,15 +2603,15 @@ path = Tasks; sourceTree = ""; }; - 618725A92C29F09900987354 /* Notificaitons */ = { + 618725A92C29F09900987354 /* Notifications */ = { isa = PBXGroup; children = ( 617DB3D52C25B02D00B58BFE /* TaskNotificationView.swift */, 617DB3D92C25B07F00B58BFE /* TaskNotificationsDetailView.swift */, - 617DB3D72C25B04D00B58BFE /* CustomLoadingRingView.swift */, + 617DB3D72C25B04D00B58BFE /* CECircularProgressView.swift */, 617DB3CF2C25AFAE00B58BFE /* TaskNotificationHandler.swift */, ); - path = Notificaitons; + path = Notifications; sourceTree = ""; }; 618725B92C33107C00987354 /* CEWorkspaceSettings */ = { @@ -2640,9 +2651,9 @@ 61D435C52C29684100D032B8 /* Tasks */ = { isa = PBXGroup; children = ( + B69D3EDF2C5E85B8005CF43A /* Models */, + B69D3EDC2C5E856F005CF43A /* Views */, 61D435CB2C29699800D032B8 /* TaskManager.swift */, - 61D435CD2C2969C300D032B8 /* CEActiveTask.swift */, - 61D435D12C2969D800D032B8 /* CETaskStatus.swift */, ); path = Tasks; sourceTree = ""; @@ -3224,7 +3235,6 @@ children = ( B62AEDB42A1FE295009A9F52 /* UtilityAreaDebugView.swift */, 618725B32C2DBC2800987354 /* TaskOutputActionsView.swift */, - 618725AF2C2DBBE900987354 /* TaskSidebarTileView.swift */, 618725B12C2DBC0C00987354 /* TaskOutputView.swift */, ); path = DebugUtility; @@ -3259,6 +3269,24 @@ path = Settings; sourceTree = ""; }; + B69D3EDC2C5E856F005CF43A /* Views */ = { + isa = PBXGroup; + children = ( + B69D3EDD2C5E85A2005CF43A /* StopTaskToolbarButton.swift */, + B68108032C60287F008B27C1 /* StartTaskToolbarButton.swift */, + ); + path = Views; + sourceTree = ""; + }; + B69D3EDF2C5E85B8005CF43A /* Models */ = { + isa = PBXGroup; + children = ( + 61D435CD2C2969C300D032B8 /* CEActiveTask.swift */, + 61D435D12C2969D800D032B8 /* CETaskStatus.swift */, + ); + path = Models; + sourceTree = ""; + }; B6AB09AB2AAACBF70003A3A6 /* Tabs */ = { isa = PBXGroup; children = ( @@ -3853,7 +3881,7 @@ B6C4F2AC2B3CC4D000B2B140 /* CommitChangedFileListItemView.swift in Sources */, 61A3E3E12C331B4A00076BD3 /* AddCETaskView.swift in Sources */, 6C82D6B329BFD88700495C54 /* NavigateCommands.swift in Sources */, - 617DB3D82C25B04D00B58BFE /* CustomLoadingRingView.swift in Sources */, + 617DB3D82C25B04D00B58BFE /* CECircularProgressView.swift in Sources */, B6CFD8112C20A8EE00E63F1A /* NSFont+WithWeight.swift in Sources */, B66A4E4C29C9179B004573B4 /* CodeEditApp.swift in Sources */, 661EF7B82BEE215300C3E577 /* ImageFileView.swift in Sources */, @@ -3933,6 +3961,7 @@ 6C147C4D29A32AA30089B630 /* EditorAreaView.swift in Sources */, B6152B802ADAE421004C6012 /* CodeEditWindowControllerExtensions.swift in Sources */, 587B9E7B29301D8F00AC7927 /* GitHubRouter.swift in Sources */, + B68108042C60287F008B27C1 /* StartTaskToolbarButton.swift in Sources */, 201169E22837B3D800F92B46 /* SourceControlNavigatorChangesView.swift in Sources */, 850C631029D6B01D00E1444C /* SettingsView.swift in Sources */, 77A01E6D2BC3EA2A00F0EA38 /* NSWindow+Child.swift in Sources */, @@ -3948,6 +3977,7 @@ 6CC17B512C43311900834E2C /* ProjectNavigatorViewController+NSOutlineViewDataSource.swift in Sources */, 587B9E6429301D8F00AC7927 /* GitLabCommit.swift in Sources */, B6E55C3B2A95368E003ECC7D /* EditorTabsOverflowShadow.swift in Sources */, + B69D3EE12C5F5357005CF43A /* TaskView.swift in Sources */, 58A5DFA229339F6400D1BD5D /* KeybindingManager.swift in Sources */, B62AEDB32A1FD95B009A9F52 /* UtilityAreaTerminalView.swift in Sources */, 661EF7BD2BEE215300C3E577 /* LoadingFileView.swift in Sources */, @@ -3997,6 +4027,7 @@ 587B9E6329301D8F00AC7927 /* GitLabAccount.swift in Sources */, 6C1CC99B2B1E7CBC0002349B /* FindNavigatorIndexBar.swift in Sources */, 285FEC7027FE4B9800E57D53 /* ProjectNavigatorTableViewCell.swift in Sources */, + B69D3EE32C5F536B005CF43A /* ActiveTaskView.swift in Sources */, 300051672BBD3A5D00A98562 /* ServiceContainer.swift in Sources */, 6CB9144B29BEC7F100BC47F2 /* (null) in Sources */, 587B9E7429301D8F00AC7927 /* URL+URLParameters.swift in Sources */, @@ -4044,6 +4075,7 @@ 587B9E9929301D8F00AC7927 /* GitChangedFile.swift in Sources */, 6C147C4B29A32A7B0089B630 /* Environment+SplitEditor.swift in Sources */, 2897E1C72979A29200741E32 /* TrackableScrollView.swift in Sources */, + B69D3EDE2C5E85A2005CF43A /* StopTaskToolbarButton.swift in Sources */, 58F2EB0E292FB2B0004A9BDE /* SoftwareUpdater.swift in Sources */, 587B9E9529301D8F00AC7927 /* BitBucketUser.swift in Sources */, 587B9E7C29301D8F00AC7927 /* GitHubRepositoryRouter.swift in Sources */, @@ -4067,7 +4099,6 @@ 587B9DA629300ABD00AC7927 /* ToolbarBranchPicker.swift in Sources */, 6C6BD6F629CD145F00235D17 /* ExtensionInfo.swift in Sources */, 04BA7C202AE2D92B00584E1C /* GitClient+Status.swift in Sources */, - 618725B02C2DBBE900987354 /* TaskSidebarTileView.swift in Sources */, 58F2EB05292FB2B0004A9BDE /* Settings.swift in Sources */, 6CBD1BC62978DE53006639D5 /* Font+Caption3.swift in Sources */, 30E6D0012A6E505200A58B20 /* NavigatorSidebarViewModel.swift in Sources */, @@ -4201,6 +4232,7 @@ 611192002B08CCD700D4459B /* SearchIndexer+Memory.swift in Sources */, 587B9E8129301D8F00AC7927 /* PublicKey.swift in Sources */, 611191FE2B08CCD200D4459B /* SearchIndexer+File.swift in Sources */, + B69D3EE52C5F54B3005CF43A /* TasksPopoverMenuItem.swift in Sources */, 669A50532C380C8E00304CD8 /* Collection+subscript_safe.swift in Sources */, 5B241BF32B6DDBFF0016E616 /* IgnorePatternListItemView.swift in Sources */, 6CB52DC92AC8DC3E002E75B3 /* CEWorkspaceFileManager+FileManagement.swift in Sources */, diff --git a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift index e96d73bad..ecac0f94a 100644 --- a/CodeEdit/Features/ActivityViewer/ActivityViewer.swift +++ b/CodeEdit/Features/ActivityViewer/ActivityViewer.swift @@ -46,11 +46,14 @@ struct ActivityViewer: View { .fixedSize() } .fixedSize(horizontal: false, vertical: false) - .padding(.horizontal, 10) + .padding(.horizontal, 5) + .padding(.vertical, 1.5) + .frame(height: 22) + .clipped() .background { if colorScheme == .dark { RoundedRectangle(cornerRadius: 5) - .opacity(0.10) + .opacity(0.1) } else { RoundedRectangle(cornerRadius: 5) .opacity(0.1) diff --git a/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationView.swift b/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationView.swift deleted file mode 100644 index 4c1138313..000000000 --- a/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationView.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// TaskNotificationView.swift -// CodeEdit -// -// Created by Tommy Ludwig on 21.06.24. -// - -import SwiftUI - -struct TaskNotificationView: View { - @ObservedObject var taskNotificationHandler: TaskNotificationHandler - @State private var hovered: Bool = false - @State private var isPresented: Bool = false - - var body: some View { - if let notification = taskNotificationHandler.notifications.first { - HStack { - Text(notification.title) - .font(.subheadline) - - if notification.isLoading { - CustomLoadingRingView( - progress: notification.percentage, - currentTaskCount: taskNotificationHandler.notifications.count - ) - .padding(.leading, 5) - .frame(height: 15) - } else { - if taskNotificationHandler.notifications.count > 1 { - Text("\(taskNotificationHandler.notifications.count)") - .font(.caption) - .padding(5) - .background( - Circle() - .foregroundStyle(.gray) - .opacity(0.3) - ) - .padding(-5) - } - } - } - .animation(.easeInOut, value: notification) - .padding(3) - .background { - if hovered { - RoundedRectangle(cornerRadius: 5) - .tint(.gray) - .foregroundStyle(.ultraThickMaterial) - } - } - .onHover { isHovered in - self.hovered = isHovered - } - .padding(-3) - .popover(isPresented: $isPresented) { - TaskNotificationsDetailView(taskNotificationHandler: taskNotificationHandler) - }.onTapGesture { - self.isPresented.toggle() - } - } - } -} - -#Preview { - TaskNotificationView(taskNotificationHandler: TaskNotificationHandler()) -} diff --git a/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationsDetailView.swift b/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationsDetailView.swift deleted file mode 100644 index 9f11d20d4..000000000 --- a/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationsDetailView.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// TaskNotificationsDetailView.swift -// CodeEdit -// -// Created by Tommy Ludwig on 21.06.24. -// - -import SwiftUI - -struct TaskNotificationsDetailView: View { - @ObservedObject var taskNotificationHandler: TaskNotificationHandler - @State private var selectedTaskNotificationIndex: Int = 0 - var body: some View { - ScrollView { - VStack(alignment: .leading) { - if let selected = - taskNotificationHandler - .notifications[safe: selectedTaskNotificationIndex] { - Text(selected.title) - .font(.headline) - - Text(selected.id) - .foregroundStyle(.secondary) - .font(.system(size: 8)) - - Divider() - .padding(.vertical, 5) - - if let message = selected.message, !message.isEmpty { - Text(message) - .fixedSize(horizontal: false, vertical: true) - .transition(.identity) - } else { - Text("No Details") - } - - if selected.isLoading { - if let percentage = selected.percentage { - ProgressView(value: percentage) { - // Text("Progress") - } currentValueLabel: { - Text("\(String(format: "%.0f", percentage * 100))%") - }.padding(.top, 5) - } else { - ProgressView() - .progressViewStyle(.linear) - .padding(.top, 5) - } - } - - Spacer() - Divider() - - HStack { - Button(action: { - withAnimation { - selectedTaskNotificationIndex -= 1 - } - }, label: { - Image(systemName: "chevron.left") - }) - .disabled( - selectedTaskNotificationIndex - 1 < 0 - ) - - Spacer() - - Text("\(selectedTaskNotificationIndex + 1)") - - Spacer() - - Button(action: { - withAnimation { - selectedTaskNotificationIndex += 1 - } - }, label: { - Image(systemName: "chevron.right") - }) - .disabled( - selectedTaskNotificationIndex + 1 == taskNotificationHandler.notifications.count - ) - }.animation(.spring, value: selected) - } else { - Text("Task done") - .font(.headline) - - Divider() - .padding(.vertical, 5) - - Text("The task has been deleted and is no longer available.") - .fixedSize(horizontal: false, vertical: true) - .transition(.identity) - } - } - } - .padding(5) - .frame(width: 220) - .onChange(of: taskNotificationHandler.notifications) { newValue in - if selectedTaskNotificationIndex >= newValue.count { - selectedTaskNotificationIndex = 0 - } - } - } -} - -#Preview { - TaskNotificationsDetailView(taskNotificationHandler: TaskNotificationHandler()) -} diff --git a/CodeEdit/Features/ActivityViewer/Notificaitons/CustomLoadingRingView.swift b/CodeEdit/Features/ActivityViewer/Notifications/CECircularProgressView.swift similarity index 77% rename from CodeEdit/Features/ActivityViewer/Notificaitons/CustomLoadingRingView.swift rename to CodeEdit/Features/ActivityViewer/Notifications/CECircularProgressView.swift index a7d04f087..df8289a83 100644 --- a/CodeEdit/Features/ActivityViewer/Notificaitons/CustomLoadingRingView.swift +++ b/CodeEdit/Features/ActivityViewer/Notifications/CECircularProgressView.swift @@ -1,5 +1,5 @@ // -// CustomLoadingRingView.swift +// CECircularProgressView.swift // CodeEdit // // Created by Tommy Ludwig on 21.06.24. @@ -7,13 +7,15 @@ import SwiftUI -struct CustomLoadingRingView: View { +struct CECircularProgressView: View { @State private var isAnimating = false @State private var previousValue: Bool = false + var progress: Double? - var currentTaskCount: Int + var currentTaskCount: Int = 1 let lineWidth: CGFloat = 2 + var body: some View { Circle() .stroke(style: StrokeStyle(lineWidth: lineWidth)) @@ -22,12 +24,12 @@ struct CustomLoadingRingView: View { if let progress = progress { Circle() .trim(from: 0, to: progress) - .stroke(Color.blue.gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) + .stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) .animation(.easeInOut, value: progress) } else { Circle() .trim(from: 0, to: 0.5) - .stroke(Color.blue.gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) + .stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) .rotationEffect( previousValue ? .degrees(isAnimating ? 0 : -360) @@ -41,6 +43,7 @@ struct CustomLoadingRingView: View { } } .rotationEffect(.degrees(-90)) + .padding(lineWidth/2) .overlay { if currentTaskCount > 1 { Text("\(currentTaskCount)") @@ -52,10 +55,10 @@ struct CustomLoadingRingView: View { #Preview { Group { - CustomLoadingRingView(currentTaskCount: 1) + CECircularProgressView(currentTaskCount: 1) .frame(width: 22, height: 22) - CustomLoadingRingView(progress: 0.65, currentTaskCount: 1) + CECircularProgressView(progress: 0.65, currentTaskCount: 1) .frame(width: 22, height: 22) } .padding() diff --git a/CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationHandler.swift b/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationHandler.swift similarity index 100% rename from CodeEdit/Features/ActivityViewer/Notificaitons/TaskNotificationHandler.swift rename to CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationHandler.swift diff --git a/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationView.swift b/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationView.swift new file mode 100644 index 000000000..d86df90d9 --- /dev/null +++ b/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationView.swift @@ -0,0 +1,71 @@ +// +// TaskNotificationView.swift +// CodeEdit +// +// Created by Tommy Ludwig on 21.06.24. +// + +import SwiftUI + +struct TaskNotificationView: View { + @ObservedObject var taskNotificationHandler: TaskNotificationHandler + @State private var isPresented: Bool = false + @State var notification: TaskNotificationModel? + + var body: some View { + ZStack { + if let notification { + HStack { + Text(notification.title) + .font(.subheadline) + .transition( + .asymmetric(insertion: .move(edge: .top), removal: .move(edge: .bottom)) + .combined(with: .opacity) + ) + .id("NotificationTitle" + notification.title) + + if notification.isLoading { + CECircularProgressView( + progress: notification.percentage, + currentTaskCount: taskNotificationHandler.notifications.count + ) + .padding(.horizontal, -1) + .frame(height: 16) + } else { + if taskNotificationHandler.notifications.count > 1 { + Text("\(taskNotificationHandler.notifications.count)") + .font(.caption) + .padding(5) + .background( + Circle() + .foregroundStyle(.gray) + .opacity(0.2) + ) + .padding(-5) + } + } + } + .transition(.opacity.combined(with: .move(edge: .trailing))) + .padding(3) + .padding(-3) + .padding(.trailing, 3) + .popover(isPresented: $isPresented, arrowEdge: .bottom) { + TaskNotificationsDetailView(taskNotificationHandler: taskNotificationHandler) + }.onTapGesture { + self.isPresented.toggle() + } + } + } + .animation(.easeInOut, value: notification) + .onChange(of: taskNotificationHandler.notifications) { newValue in + withAnimation { + notification = newValue.first + } + } + } + +} + +#Preview { + TaskNotificationView(taskNotificationHandler: TaskNotificationHandler()) +} diff --git a/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationsDetailView.swift b/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationsDetailView.swift new file mode 100644 index 000000000..4742fa817 --- /dev/null +++ b/CodeEdit/Features/ActivityViewer/Notifications/TaskNotificationsDetailView.swift @@ -0,0 +1,48 @@ +// +// TaskNotificationsDetailView.swift +// CodeEdit +// +// Created by Tommy Ludwig on 21.06.24. +// + +import SwiftUI + +struct TaskNotificationsDetailView: View { + @ObservedObject var taskNotificationHandler: TaskNotificationHandler + @State private var selectedTaskNotificationIndex: Int = 0 + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 15) { + ForEach(taskNotificationHandler.notifications, id: \.id) { notification in + HStack(alignment: .center, spacing: 8) { + CECircularProgressView(progress: notification.percentage) + .frame(width: 16, height: 16) + VStack(alignment: .leading) { + Text(notification.title) + .fixedSize(horizontal: false, vertical: true) + .transition(.identity) + + if let message = notification.message, !message.isEmpty { + Text(message) + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + Spacer() + } + } + } + } + .padding(15) + .frame(minWidth: 320) + .onChange(of: taskNotificationHandler.notifications) { newValue in + if selectedTaskNotificationIndex >= newValue.count { + selectedTaskNotificationIndex = 0 + } + } + } +} + +#Preview { + TaskNotificationsDetailView(taskNotificationHandler: TaskNotificationHandler()) +} diff --git a/CodeEdit/Features/ActivityViewer/Tasks/ActiveTaskView.swift b/CodeEdit/Features/ActivityViewer/Tasks/ActiveTaskView.swift new file mode 100644 index 000000000..eab82ba5f --- /dev/null +++ b/CodeEdit/Features/ActivityViewer/Tasks/ActiveTaskView.swift @@ -0,0 +1,21 @@ +// +// ActiveTaskView.swift +// CodeEdit +// +// Created by Austin Condiff on 8/4/24. +// + +import SwiftUI + +// We need to observe each active task individually because: +// 1. Active tasks are nested inside TaskManager. +// 2. Reference types (like objects) do not notify observers when their internal state changes. +/// `ActiveTaskView` represents a single active task and observes its state. +/// - Parameter activeTask: The active task to be displayed and observed. +struct ActiveTaskView: View { + @ObservedObject var activeTask: CEActiveTask + + var body: some View { + TaskView(task: activeTask.task, status: activeTask.status) + } +} diff --git a/CodeEdit/Features/ActivityViewer/Tasks/DropdownMenuItemStyleModifier.swift b/CodeEdit/Features/ActivityViewer/Tasks/DropdownMenuItemStyleModifier.swift index e71f4e197..859a7ade2 100644 --- a/CodeEdit/Features/ActivityViewer/Tasks/DropdownMenuItemStyleModifier.swift +++ b/CodeEdit/Features/ActivityViewer/Tasks/DropdownMenuItemStyleModifier.swift @@ -12,7 +12,11 @@ struct DropdownMenuItemStyleModifier: ViewModifier { func body(content: Content) -> some View { content - .background(isHovering ? Color(NSColor.systemBlue) : .clear) + .background( + isHovering + ? AnyView(EffectView(.selection, blendingMode: .withinWindow, emphasized: true)) + : AnyView(Color.clear) + ) .foregroundColor(isHovering ? Color(NSColor.white) : .primary) .onHover(perform: { hovering in self.isHovering = hovering diff --git a/CodeEdit/Features/ActivityViewer/Tasks/OptionMenuItemView.swift b/CodeEdit/Features/ActivityViewer/Tasks/OptionMenuItemView.swift index 9696362ad..a00c8ecdb 100644 --- a/CodeEdit/Features/ActivityViewer/Tasks/OptionMenuItemView.swift +++ b/CodeEdit/Features/ActivityViewer/Tasks/OptionMenuItemView.swift @@ -15,7 +15,7 @@ struct OptionMenuItemView: View { Text(label) Spacer() } - .padding(.vertical, 5) + .padding(.vertical, 4) .padding(.horizontal, 28) .modifier(DropdownMenuItemStyleModifier()) .clipShape(RoundedRectangle(cornerRadius: 5)) @@ -26,7 +26,7 @@ struct OptionMenuItemView: View { } #Preview { - OptionMenuItemView(label: "Tst") { + OptionMenuItemView(label: "Test") { print("test") } } diff --git a/CodeEdit/Features/ActivityViewer/Tasks/SchemeDropDownView.swift b/CodeEdit/Features/ActivityViewer/Tasks/SchemeDropDownView.swift index 01143ec37..29c60a461 100644 --- a/CodeEdit/Features/ActivityViewer/Tasks/SchemeDropDownView.swift +++ b/CodeEdit/Features/ActivityViewer/Tasks/SchemeDropDownView.swift @@ -17,22 +17,24 @@ struct SchemeDropDownView: View { @ObservedObject var workspaceSettingsManager: CEWorkspaceSettings var workspaceFileManager: CEWorkspaceFileManager? + var workspaceName: String { + workspaceSettingsManager.settings.project.projectName + } + var body: some View { - HStack(spacing: 5) { + HStack(spacing: 6) { Image(systemName: "folder.badge.gearshape") .imageScale(.medium) - - Group { - if workspaceSettingsManager.settings.project.projectName.isEmpty { - Text(workspaceFileManager?.workspaceItem.fileName() ?? "No Project found") - } else { - Text(workspaceSettingsManager.settings.project.projectName) - } - }.font(.subheadline) + Text( + workspaceName.isEmpty + ? (workspaceFileManager?.workspaceItem.fileName() ?? "No Project found") + : workspaceName + ) } - .font(.caption) - .padding(.trailing, 9) - .padding(5) + .font(.subheadline) + .padding(.trailing, 11.5) + .padding(.horizontal, 2.5) + .padding(.vertical, 2.5) .background { Color(nsColor: colorScheme == .dark ? .white : .black) .opacity(isHoveringScheme || isSchemePopOverPresented ? 0.05 : 0) @@ -41,10 +43,10 @@ struct SchemeDropDownView: View { Spacer() if isHoveringScheme || isSchemePopOverPresented { chevronDown - .padding(.trailing, 3) + .padding(.trailing, 2) } else { chevron - .padding(.trailing, 3) + .padding(.trailing, 4) } } } @@ -57,13 +59,12 @@ struct SchemeDropDownView: View { workspaceFileManager: workspaceFileManager, item: workspaceFileManager?.workspaceItem ) - Divider() .padding(.vertical, 5) Group { - OptionMenuItemView(label: "Add Folder..") { + OptionMenuItemView(label: "Add Folder...") { // TODO: Implment Add Folder - print("NOT IMPLMENTED") + print("NOT IMPLEMENTED") } OptionMenuItemView(label: "Workspace Settings...") { NSApp.sendAction( @@ -72,8 +73,11 @@ struct SchemeDropDownView: View { } } } + .font(.subheadline) .padding(5) - }.onTapGesture { + .frame(minWidth: 215) + } + .onTapGesture { self.isSchemePopOverPresented.toggle() } } @@ -90,7 +94,7 @@ struct SchemeDropDownView: View { VStack(spacing: 1) { Image(systemName: "chevron.down") } - .font(.system(size: 8, weight: .bold, design: .default)) + .font(.system(size: 8, weight: .semibold, design: .default)) .padding(.top, 0.5) } } diff --git a/CodeEdit/Features/ActivityViewer/Tasks/TaskDropDownView.swift b/CodeEdit/Features/ActivityViewer/Tasks/TaskDropDownView.swift index 29175f183..72de62545 100644 --- a/CodeEdit/Features/ActivityViewer/Tasks/TaskDropDownView.swift +++ b/CodeEdit/Features/ActivityViewer/Tasks/TaskDropDownView.swift @@ -20,17 +20,20 @@ struct TaskDropDownView: View { Group { if let selectedTask = taskManager.selectedTask { if let selectedActiveTask = taskManager.activeTasks[selectedTask.id] { - TaskView(activeTask: selectedActiveTask, isCompact: true) + ActiveTaskView(activeTask: selectedActiveTask) + .fixedSize() } else { - DefaultTaskView(task: selectedTask) + TaskView(task: selectedTask, status: CETaskStatus.notRunning) + .fixedSize() } } else { Text("Create Tasks") - .font(.subheadline) } } - .padding(.trailing, 9) - .padding(5) + .font(.subheadline) + .padding(.trailing, 11.5) + .padding(.horizontal, 2.5) + .padding(.vertical, 2.5) .background(backgroundColor) .onHover { hovering in self.isHoveringTasks = hovering @@ -58,141 +61,30 @@ struct TaskDropDownView: View { } private var chevronIcon: some View { - VStack(spacing: 1) { - Image(systemName: "chevron.down") - .font(.system(size: 8, weight: .bold, design: .default)) - .padding(.top, 0.5) - .padding(.trailing, 3) - } + Image(systemName: "chevron.down") + .font(.system(size: 8, weight: .bold, design: .default)) + .padding(.top, 0.5) + .padding(.trailing, 2) } private var taskPopoverContent: some View { VStack(alignment: .leading, spacing: 0) { if !taskManager.availableTasks.isEmpty { ForEach(taskManager.availableTasks, id: \.id) { task in - TasksPopoverView(taskManager: taskManager, task: task) + TasksPopoverMenuItem(taskManager: taskManager, task: task) } Divider() .padding(.vertical, 5) } - - Group { - OptionMenuItemView(label: "Add Task..") { - NSApp.sendAction(#selector(CodeEditWindowController.openWorkspaceSettings(_:)), to: nil, from: nil) - } - OptionMenuItemView(label: "Manage Tasks...") { - NSApp.sendAction(#selector(CodeEditWindowController.openWorkspaceSettings(_:)), to: nil, from: nil) - } - } - } - .padding(5) - .frame(width: 215) - } - - private struct DefaultTaskView: View { - @ObservedObject var task: CETask - var body: some View { - HStack(spacing: 3) { - Image(systemName: "gearshape") - .imageScale(.medium) - - Text(task.name) - .font(.subheadline) - - Circle() - .fill(CETaskStatus.notRunning.color) - .frame(width: 5, height: 5) + OptionMenuItemView(label: "Add Task...") { + NSApp.sendAction(#selector(CodeEditWindowController.openWorkspaceSettings(_:)), to: nil, from: nil) } - } - } -} - -// We need to observe each active task individually because: -// 1. Active tasks are nested inside TaskManager. -// 2. Reference types (like objects) do not notify observers when their internal state changes. -/// `TaskView` represents a single active task and observes its state. -/// - Parameter activeTask: The active task to be displayed and observed. -/// - Parameter isCompact: Determines the layout style of the view. -/// Set to `true` for a compact display, used for the current task in the activity viewer. -/// Set to `false`, used in the popover. -struct TaskView: View { - @ObservedObject var activeTask: CEActiveTask - // This property allows for compact layout adjustment - var isCompact: Bool - - var body: some View { - HStack(spacing: isCompact ? 3 : 8) { - Image(systemName: "gearshape") - .imageScale(.medium) - - Text(activeTask.task.name) - .font(isCompact ? .subheadline : .body) - - if !isCompact { - Spacer() + OptionMenuItemView(label: "Manage Tasks...") { + NSApp.sendAction(#selector(CodeEditWindowController.openWorkspaceSettings(_:)), to: nil, from: nil) } - - Circle() - .fill(activeTask.status.color) - .frame(width: 5, height: 5) - } - } -} - -struct TasksPopoverView: View { - @Environment(\.dismiss) - private var dismiss - - @ObservedObject var taskManager: TaskManager - var task: CETask - - var body: some View { - HStack { - selectionIndicator - popoverContent - } - .padding(.vertical, 5) - .padding(.horizontal, 10) - .modifier(DropdownMenuItemStyleModifier()) - .onTapGesture { - taskManager.selectedTaskID = task.id - dismiss() - } - .clipShape(RoundedRectangle(cornerRadius: 5)) - } - - private var selectionIndicator: some View { - Group { - if taskManager.selectedTaskID == task.id { - Image(systemName: "checkmark") - .imageScale(.small) - .frame(width: 10) - } else { - Spacer() - .frame(width: 18) - } - } - } - - private var popoverContent: some View { - Group { - if let activeTask = taskManager.activeTasks[task.id] { - TaskView(activeTask: activeTask, isCompact: false) - } else { - defaultTaskView - } - } - } - - private var defaultTaskView: some View { - HStack { - Image(systemName: "gearshape") - .imageScale(.medium) - Text(task.name) - Spacer() - Circle() - .fill(taskManager.taskStatus(taskID: task.id).color) - .frame(width: 5, height: 5) } + .font(.subheadline) + .padding(5) + .frame(minWidth: 215) } } diff --git a/CodeEdit/Features/ActivityViewer/Tasks/TaskView.swift b/CodeEdit/Features/ActivityViewer/Tasks/TaskView.swift new file mode 100644 index 000000000..92afa0726 --- /dev/null +++ b/CodeEdit/Features/ActivityViewer/Tasks/TaskView.swift @@ -0,0 +1,31 @@ +// +// TaskView.swift +// CodeEdit +// +// Created by Austin Condiff on 8/4/24. +// + +import SwiftUI + +/// `TaskView` represents a single active task and observes its state. +/// - Parameter task: The task to be displayed and observed. +/// - Parameter status: The status of the task to be displayed. +struct TaskView: View { + @ObservedObject var task: CETask + var status: CETaskStatus + + var body: some View { + HStack(spacing: 5) { + Image(systemName: "gearshape") + Text(task.name) + Spacer(minLength: 0) + } + .padding(.trailing, 7.5) + .overlay(alignment: .trailing) { + Circle() + .fill(status.color) + .frame(width: 5, height: 5) + .padding(.trailing, 2.5) + } + } +} diff --git a/CodeEdit/Features/ActivityViewer/Tasks/TasksPopoverMenuItem.swift b/CodeEdit/Features/ActivityViewer/Tasks/TasksPopoverMenuItem.swift new file mode 100644 index 000000000..0cb3a02c2 --- /dev/null +++ b/CodeEdit/Features/ActivityViewer/Tasks/TasksPopoverMenuItem.swift @@ -0,0 +1,55 @@ +// +// TasksPopoverMenuItem.swift +// CodeEdit +// +// Created by Austin Condiff on 8/4/24. +// + +import SwiftUI + +struct TasksPopoverMenuItem: View { + @Environment(\.dismiss) + private var dismiss + + @ObservedObject var taskManager: TaskManager + var task: CETask + + var body: some View { + HStack(spacing: 5) { + selectionIndicator + popoverContent + } + .padding(.vertical, 4) + .padding(.horizontal, 8) + .modifier(DropdownMenuItemStyleModifier()) + .onTapGesture { + taskManager.selectedTaskID = task.id + dismiss() + } + .clipShape(RoundedRectangle(cornerRadius: 5)) + } + + private var selectionIndicator: some View { + Group { + if taskManager.selectedTaskID == task.id { + Image(systemName: "checkmark") + .fontWeight(.bold) + .imageScale(.small) + .frame(width: 10) + } else { + Spacer() + .frame(width: 10) + } + } + } + + private var popoverContent: some View { + Group { + if let activeTask = taskManager.activeTasks[task.id] { + ActiveTaskView(activeTask: activeTask) + } else { + TaskView(task: task, status: taskManager.taskStatus(taskID: task.id)) + } + } + } +} diff --git a/CodeEdit/Features/ActivityViewer/Tasks/WorkspaceMenuItemView.swift b/CodeEdit/Features/ActivityViewer/Tasks/WorkspaceMenuItemView.swift index cb6216e02..853bcdc95 100644 --- a/CodeEdit/Features/ActivityViewer/Tasks/WorkspaceMenuItemView.swift +++ b/CodeEdit/Features/ActivityViewer/Tasks/WorkspaceMenuItemView.swift @@ -12,22 +12,23 @@ struct WorkspaceMenuItemView: View { var item: CEWorkspaceFile? var body: some View { - HStack { + HStack(spacing: 5) { if workspaceFileManager?.workspaceItem.fileName() == item?.name { Image(systemName: "checkmark") + .fontWeight(.bold) .imageScale(.small) .frame(width: 10) } else { Spacer() - .frame(width: 18) + .frame(width: 10) } Image(systemName: "folder.badge.gearshape") .imageScale(.medium) Text(item?.name ?? "") Spacer() } - .padding(.vertical, 5) - .padding(.horizontal, 10) + .padding(.vertical, 4) + .padding(.horizontal, 8) .modifier(DropdownMenuItemStyleModifier()) .onTapGesture { } .clipShape(RoundedRectangle(cornerRadius: 5)) diff --git a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/AddCETaskView.swift b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/AddCETaskView.swift index a66ff5522..577c5b3e9 100644 --- a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/AddCETaskView.swift +++ b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/AddCETaskView.swift @@ -18,29 +18,29 @@ struct AddCETaskView: View { self._newTask = StateObject(wrappedValue: CETask(target: "My Mac", workingDirectory: workingDirectory)) } var body: some View { - // TODO: Discuss if this is needed - NavigationStack { - VStack { - CETaskFormView( - task: newTask - ).padding(.top) - + VStack(spacing: 0) { + CETaskFormView(task: newTask) + Divider() + HStack { + Button { + dismiss() + } label: { + Text("Cancel") + .frame(minWidth: 56) + } Spacer() - Divider() - HStack { - Button("Cancel") { - dismiss() - } - Spacer() - Button("Save") { - workspaceSettingsManager.settings.tasks.append(newTask) - try? workspaceSettingsManager.savePreferences() - dismiss() - } - .disabled(newTask.isInvalid) + Button { + workspaceSettingsManager.settings.tasks.append(newTask) + try? workspaceSettingsManager.savePreferences() + dismiss() + } label: { + Text("Save") + .frame(minWidth: 56) } - .padding() - }.navigationTitle("Add Task") + .buttonStyle(.borderedProminent) + .disabled(newTask.isInvalid) + } + .padding() } } diff --git a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CETaskFormView.swift b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CETaskFormView.swift index e22a2b4a3..ea4159450 100644 --- a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CETaskFormView.swift +++ b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CETaskFormView.swift @@ -14,7 +14,7 @@ struct CETaskFormView: View { @StateObject var settingsViewModel = SettingsViewModel() var body: some View { - SettingsForm { + Form { Section { TextField(text: $task.name) { Text("Name") @@ -80,7 +80,9 @@ struct CETaskFormView: View { } header: { Text("Environment Variables") } - }.environmentObject(settingsViewModel) + } + .formStyle(.grouped) + .environmentObject(settingsViewModel) } func removeSelectedEnv() { diff --git a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CEWorkspaceSettingsView.swift b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CEWorkspaceSettingsView.swift index 49cf6a669..70772e47d 100644 --- a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CEWorkspaceSettingsView.swift +++ b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/CEWorkspaceSettingsView.swift @@ -8,6 +8,8 @@ import SwiftUI struct CEWorkspaceSettingsView: View { + var dismiss: () -> Void + @EnvironmentObject var workspaceSettingsManager: CEWorkspaceSettings @EnvironmentObject var workspace: WorkspaceDocument @@ -16,10 +18,9 @@ struct CEWorkspaceSettingsView: View { @State var selectedTaskID: UUID? @State var showAddTaskSheet: Bool = false - let window: NSWindow? var body: some View { VStack(spacing: 0) { - SettingsForm { + Form { Section { TextField( "Name", @@ -49,16 +50,21 @@ struct CEWorkspaceSettingsView: View { } } } - .scrollDisabled(true) + .formStyle(.grouped) + .scrollContentBackground(.hidden) - Spacer() Divider() HStack { Spacer() - Button("Done") { - window?.close() + Button { + dismiss() + } label: { + Text("Done") + .frame(minWidth: 56) } - }.padding() + .buttonStyle(.borderedProminent) + } + .padding() } .environmentObject(settingsViewModel) .sheet(isPresented: $showAddTaskSheet) { @@ -77,5 +83,5 @@ struct CEWorkspaceSettingsView: View { } #Preview { - CEWorkspaceSettingsView(window: nil) + CEWorkspaceSettingsView(dismiss: { print("Dismiss") }) } diff --git a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/EditCETaskView.swift b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/EditCETaskView.swift index c4264bae9..179c92c1d 100644 --- a/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/EditCETaskView.swift +++ b/CodeEdit/Features/CEWorkSpaceSettings/CEWorkspaceSettings/Views/EditCETaskView.swift @@ -18,29 +18,33 @@ struct EditCETaskView: View { let selectedTaskIndex: Int var body: some View { - VStack { - Text("Edit Task") + VStack(spacing: 0) { CETaskFormView(task: task) - .padding(.top) - - Spacer() Divider() HStack { - Button("Delete") { + Button(role: .destructive) { workspaceSettingsManager.settings.tasks.removeAll(where: { $0.id == task.id }) try? workspaceSettingsManager.savePreferences() taskManger.deleteTask(taskID: task.id) self.dismiss() + } label: { + Text("Delete") + .foregroundStyle(.red) + .frame(minWidth: 56) } Spacer() - Button("Done") { + Button { try? workspaceSettingsManager.savePreferences() self.dismiss() + } label: { + Text("Done") + .frame(minWidth: 56) } + .buttonStyle(.borderedProminent) .disabled(task.isInvalid) } .padding() diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift index d1d92b471..b0d612b3e 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController+Toolbar.swift @@ -7,6 +7,7 @@ import AppKit import SwiftUI +import Combine extension CodeEditWindowController { internal func setupToolbar() { @@ -67,7 +68,7 @@ extension CodeEditWindowController { } } - // swiftlint:disable:next function_body_length + // swiftlint:disable:next function_body_length cyclomatic_complexity func toolbar( _ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, @@ -112,30 +113,27 @@ extension CodeEditWindowController { return toolbarItem case .stopTaskSidebarItem: let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.stopTaskSidebarItem) - toolbarItem.label = "Stop" - toolbarItem.paletteLabel = "Stop" - toolbarItem.toolTip = "Stop selected task" - toolbarItem.isBordered = true - toolbarItem.target = self - toolbarItem.action = #selector(self.terminateActiveTask) - toolbarItem.image = NSImage( - systemSymbolName: "stop.fill", - accessibilityDescription: nil - )?.withSymbolConfiguration(.init(scale: .large)) + + guard let taskManager = workspace?.taskManager + else { return nil } + + let view = NSHostingView( + rootView: StopTaskToolbarButton(taskManager: taskManager) + ) + toolbarItem.view = view return toolbarItem case .startTaskSidebarItem: let toolbarItem = NSToolbarItem(itemIdentifier: NSToolbarItem.Identifier.startTaskSidebarItem) - toolbarItem.label = "Start" - toolbarItem.paletteLabel = "Start" - toolbarItem.toolTip = "Start selected task" - toolbarItem.isBordered = true - toolbarItem.target = self - toolbarItem.action = #selector(self.runActiveTask) - toolbarItem.image = NSImage( - systemSymbolName: "play.fill", - accessibilityDescription: nil - )?.withSymbolConfiguration(.init(scale: .large)) + + guard let taskManager = workspace?.taskManager else { return nil } + guard let workspace = workspace else { return nil } + + let view = NSHostingView( + rootView: StartTaskToolbarButton(taskManager: taskManager) + .environmentObject(workspace) + ) + toolbarItem.view = view return toolbarItem case .branchPicker: @@ -181,16 +179,4 @@ extension CodeEditWindowController { return NSToolbarItem(itemIdentifier: itemIdentifier) } } - - @objc - private func runActiveTask() { - guard let taskManager = workspace?.taskManager else { return } - taskManager.executeActiveTask() - } - - @objc - private func terminateActiveTask() { - guard let taskManager = workspace?.taskManager else { return } - taskManager.terminateActiveTask() - } } diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift index 1c47aca56..6c487cefd 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift @@ -112,17 +112,21 @@ extension CodeEditWindowController { } else { let settingsWindow = NSWindow() self.workspaceSettingsWindow = settingsWindow - let contentView = CEWorkspaceSettingsView(window: settingsWindow) - .environmentObject(workspaceSettingsManager) - .environmentObject(workspace) - .environmentObject(taskManager) - - settingsWindow.contentView = NSHostingView(rootView: contentView) - settingsWindow.titlebarAppearsTransparent = true - settingsWindow.setContentSize(NSSize(width: 515, height: 515)) - settingsWindow.makeKeyAndOrderFront(self) - - window.addCenteredChildWindow(settingsWindow, over: window) + let contentView = CEWorkspaceSettingsView( + dismiss: { [weak self, weak settingsWindow] in + guard let settingsWindow else { return } + self?.window?.endSheet(settingsWindow) + } + ) + .environmentObject(workspaceSettingsManager) + .environmentObject(workspace) + .environmentObject(taskManager) + + settingsWindow.contentView = NSHostingView(rootView: contentView) + settingsWindow.titlebarAppearsTransparent = true + settingsWindow.setContentSize(NSSize(width: 515, height: 515)) + + window.beginSheet(settingsWindow, completionHandler: nil) } } } diff --git a/CodeEdit/Features/Tasks/CEActiveTask.swift b/CodeEdit/Features/Tasks/Models/CEActiveTask.swift similarity index 97% rename from CodeEdit/Features/Tasks/CEActiveTask.swift rename to CodeEdit/Features/Tasks/Models/CEActiveTask.swift index fa1358804..56bc07c5f 100644 --- a/CodeEdit/Features/Tasks/CEActiveTask.swift +++ b/CodeEdit/Features/Tasks/Models/CEActiveTask.swift @@ -63,7 +63,7 @@ class CEActiveTask: ObservableObject, Identifiable, Hashable { process: process, command: self.task.fullCommand, environmentVariables: self.task.environmentVariablesDictionary, - shell: Shell.zsh, // TODO: Let user decide which shell he uses + shell: Shell.zsh, // TODO: Let user decide which shell to use outputPipe: outputPipe ) } catch { print(error) } @@ -72,11 +72,12 @@ class CEActiveTask: ObservableObject, Identifiable, Hashable { func handleProcessFinished(terminationStatus: Int32) async { handleTerminationStatus(terminationStatus) + if terminationStatus == 0 { await updateOutput("\nFinished running \(task.name).\n\n") await updateTaskStatus(to: .finished) updateTaskNotification( - title: "Finished Running: \(task.name)", + title: "Finished Running \(task.name)", message: "", isLoading: false ) @@ -92,7 +93,7 @@ class CEActiveTask: ObservableObject, Identifiable, Hashable { await updateOutput("\nFailed to run \(task.name).\n\n") await updateTaskStatus(to: .failed) updateTaskNotification( - title: "Failed Running: \(task.name)", + title: "Failed Running \(task.name)", message: "", isLoading: false ) @@ -141,7 +142,7 @@ class CEActiveTask: ObservableObject, Identifiable, Hashable { let userInfo: [String: Any] = [ "id": self.task.id.uuidString, "action": "createWithPriority", - "title": "Running: \(self.task.name)", + "title": "Running \(self.task.name)", "message": "Running your task: \(self.task.name).", "isLoading": true ] diff --git a/CodeEdit/Features/Tasks/CETaskStatus.swift b/CodeEdit/Features/Tasks/Models/CETaskStatus.swift similarity index 100% rename from CodeEdit/Features/Tasks/CETaskStatus.swift rename to CodeEdit/Features/Tasks/Models/CETaskStatus.swift diff --git a/CodeEdit/Features/Tasks/TaskManager.swift b/CodeEdit/Features/Tasks/TaskManager.swift index 935099c23..0ba8dda2f 100644 --- a/CodeEdit/Features/Tasks/TaskManager.swift +++ b/CodeEdit/Features/Tasks/TaskManager.swift @@ -12,6 +12,7 @@ import Combine class TaskManager: ObservableObject { @Published var activeTasks: [UUID: CEActiveTask] = [:] @Published var selectedTaskID: UUID? + @Published var taskShowingOutput: UUID? @ObservedObject var workspaceSettings: CEWorkspaceSettingsData @@ -59,22 +60,27 @@ class TaskManager: ObservableObject { func executeActiveTask() { let task = workspaceSettings.tasks.first { $0.id == selectedTaskID } guard let task else { return } - runTask(task: task) + Task { + await runTask(task: task) + } } - func runTask(task: CETask) { + func runTask(task: CETask) async { // A process can only be started once, that means we have to renew the Process and Pipe - // but don't initialise a new object. + // but don't initialize a new object. if let activeTask = activeTasks[task.id] { activeTask.renew() + // Wait until the task is no longer running. + // The termination handler is asynchronous, so we avoid a race condition using this. + while activeTask.status == .running { + await Task.yield() + } activeTask.run() } else { let runningTask = CEActiveTask(task: task) runningTask.run() - Task { - await MainActor.run { - activeTasks[task.id] = runningTask - } + await MainActor.run { + activeTasks[task.id] = runningTask } } } diff --git a/CodeEdit/Features/Tasks/Views/StartTaskToolbarButton.swift b/CodeEdit/Features/Tasks/Views/StartTaskToolbarButton.swift new file mode 100644 index 000000000..2d9315270 --- /dev/null +++ b/CodeEdit/Features/Tasks/Views/StartTaskToolbarButton.swift @@ -0,0 +1,37 @@ +// +// StartTaskToolbarButton.swift +// CodeEdit +// +// Created by Austin Condiff on 8/4/24. +// + +import SwiftUI + +struct StartTaskToolbarButton: View { + @UpdatingWindowController var windowController: CodeEditWindowController? + + @ObservedObject var taskManager: TaskManager + @EnvironmentObject var workspace: WorkspaceDocument + + var utilityAreaCollapsed: Bool { + windowController?.workspace?.utilityAreaModel?.isCollapsed ?? true + } + + var body: some View { + Button { + taskManager.executeActiveTask() + if utilityAreaCollapsed { + CommandManager.shared.executeCommand("open.drawer") + } + workspace.utilityAreaModel?.selectedTab = .debugConsole + taskManager.taskShowingOutput = taskManager.selectedTaskID + } label: { + Label("Start", systemImage: "play.fill") + .labelStyle(.iconOnly) + .font(.system(size: 18, weight: .regular)) + .help("Start selected task") + .frame(width: 28) + .offset(CGSize(width: 0, height: 2.5)) + } + } +} diff --git a/CodeEdit/Features/Tasks/Views/StopTaskToolbarButton.swift b/CodeEdit/Features/Tasks/Views/StopTaskToolbarButton.swift new file mode 100644 index 000000000..837a834b0 --- /dev/null +++ b/CodeEdit/Features/Tasks/Views/StopTaskToolbarButton.swift @@ -0,0 +1,59 @@ +// +// StopTaskToolbarButton.swift +// CodeEdit +// +// Created by Austin Condiff on 8/3/24. +// + +import SwiftUI +import Combine + +struct StopTaskToolbarButton: View { + @ObservedObject var taskManager: TaskManager + + /// Tracks the current selected task's status. Updated by `updateStatusListener` + @State private var currentSelectedStatus: CETaskStatus? + /// The listener that listens to the active task's status publisher. Is updated frequently as the active task + /// changes. + @State private var statusListener: AnyCancellable? + + var body: some View { + HStack { + if let currentSelectedStatus, currentSelectedStatus == .running { + Button { + taskManager.terminateActiveTask() + } label: { + Label("Stop", systemImage: "stop.fill") + .labelStyle(.iconOnly) + .font(.system(size: 15, weight: .regular)) + .help("Stop selected task") + .frame(width: 28) + .offset(y: 1.5) + } + .frame(height: 22) + .transition(.opacity.combined(with: .move(edge: .trailing))) + } + } + .frame(width: 38, height: 22) + .animation( + .easeInOut(duration: 0.3), + value: currentSelectedStatus + ) + .onChange(of: taskManager.selectedTaskID) { _ in updateStatusListener() } + .onChange(of: taskManager.activeTasks) { _ in updateStatusListener() } + .onAppear(perform: updateStatusListener) + .onDisappear { + statusListener?.cancel() + } + } + + /// Update the ``statusListener`` to listen to a potentially new active task. + private func updateStatusListener() { + statusListener?.cancel() + currentSelectedStatus = taskManager.activeTasks[taskManager.selectedTaskID ?? UUID()]?.status + guard let id = taskManager.selectedTaskID else { return } + statusListener = taskManager.activeTasks[id]?.$status.sink { newValue in + currentSelectedStatus = newValue + } + } +} diff --git a/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputActionsView.swift b/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputActionsView.swift index 1e7b4df49..2dbf78d5b 100644 --- a/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputActionsView.swift +++ b/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputActionsView.swift @@ -18,7 +18,9 @@ struct TaskOutputActionsView: View { Spacer() Button { - taskManager.runTask(task: activeTask.task) + Task { + await taskManager.runTask(task: activeTask.task) + } } label: { Image(systemName: "memories") .foregroundStyle(.green) diff --git a/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputView.swift b/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputView.swift index 3ba8c49fd..bd375e4fc 100644 --- a/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputView.swift +++ b/CodeEdit/Features/UtilityArea/DebugUtility/TaskOutputView.swift @@ -16,6 +16,6 @@ struct TaskOutputView: View { .textSelection(.enabled) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) - .padding() + .padding(10) } } diff --git a/CodeEdit/Features/UtilityArea/DebugUtility/TaskSidebarTileView.swift b/CodeEdit/Features/UtilityArea/DebugUtility/TaskSidebarTileView.swift deleted file mode 100644 index a421be14c..000000000 --- a/CodeEdit/Features/UtilityArea/DebugUtility/TaskSidebarTileView.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// TaskSidebarTileView.swift -// CodeEdit -// -// Created by Tommy Ludwig on 27.06.24. -// - -import SwiftUI - -struct TaskSidebarTileView: View { - @ObservedObject var activeTask: CEActiveTask - var body: some View { - HStack { - Image(systemName: "gearshape") - .imageScale(.medium) - Text(activeTask.task.name) - - Spacer() - - Circle() - .fill(activeTask.status.color) - .frame(width: 5, height: 5) - } - } -} diff --git a/CodeEdit/Features/UtilityArea/DebugUtility/UtilityAreaDebugView.swift b/CodeEdit/Features/UtilityArea/DebugUtility/UtilityAreaDebugView.swift index b6f7e5c4b..0c98f6e5f 100644 --- a/CodeEdit/Features/UtilityArea/DebugUtility/UtilityAreaDebugView.swift +++ b/CodeEdit/Features/UtilityArea/DebugUtility/UtilityAreaDebugView.swift @@ -8,12 +8,23 @@ import SwiftUI struct UtilityAreaDebugView: View { + @AppSettings(\.theme.matchAppearance) + private var matchAppearance + @AppSettings(\.terminal.darkAppearance) + private var darkAppearance + @AppSettings(\.theme.useThemeBackground) + private var useThemeBackground + + @Environment(\.colorScheme) + private var colorScheme + @EnvironmentObject private var utilityAreaViewModel: UtilityAreaViewModel @EnvironmentObject private var taskManager: TaskManager - @State var tabSelection: UUID? @State private var scrollProxy: ScrollViewProxy? + @StateObject private var themeModel: ThemeModel = .shared + @Namespace var bottomID var body: some View { @@ -27,10 +38,10 @@ struct UtilityAreaDebugView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) Spacer() } - .opacity(tabSelection == nil ? 1 : 0) + .opacity(taskManager.taskShowingOutput == nil ? 1 : 0) - if let tabSelection, - let activeTask = taskManager.activeTasks[tabSelection] { + if let taskShowingOutput = taskManager.taskShowingOutput, + let activeTask = taskManager.activeTasks[taskShowingOutput] { ScrollViewReader { proxy in VStack { ScrollView { @@ -64,7 +75,26 @@ struct UtilityAreaDebugView: View { bottomID: _bottomID ) } - .background(EffectView(.contentBackground)) + .background { + if utilityAreaViewModel.selectedTerminals.isEmpty { + EffectView(.contentBackground) + } else if useThemeBackground { + Color(nsColor: backgroundColor) + } else { + if colorScheme == .dark { + EffectView(.underPageBackground) + } else { + EffectView(.contentBackground) + } + } + } + .colorScheme( + utilityAreaViewModel.selectedTerminals.isEmpty + ? colorScheme + : matchAppearance && darkAppearance + ? themeModel.selectedDarkTheme?.appearance == .dark ? .dark : .light + : themeModel.selectedTheme?.appearance == .dark ? .dark : .light + ) } } } leadingSidebar: { _ in @@ -75,14 +105,12 @@ struct UtilityAreaDebugView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .opacity(taskManager.activeTasks.isEmpty ? 1 : 0) - List(selection: $tabSelection) { + List(selection: $taskManager.taskShowingOutput) { ForEach(Array(taskManager.activeTasks.keys), id: \.self) { taskID in if let activeTask = taskManager.activeTasks[taskID] { - TaskSidebarTileView(activeTask: activeTask) + ActiveTaskView(activeTask: activeTask) .onTapGesture { - withAnimation { - self.tabSelection = taskID - } + taskManager.taskShowingOutput = taskID } .contextMenu( ContextMenu { @@ -101,9 +129,20 @@ struct UtilityAreaDebugView: View { .paneToolbar { Spacer() } // Background } }.onReceive(taskManager.$activeTasks) { newTasks in - if tabSelection == nil { - self.tabSelection = newTasks.first?.key + if taskManager.taskShowingOutput == nil { + taskManager.taskShowingOutput = newTasks.first?.key } } } + + /// Returns the `background` color of the selected theme + private var backgroundColor: NSColor { + if let selectedTheme = matchAppearance && darkAppearance + ? themeModel.selectedDarkTheme + : themeModel.selectedTheme, + let index = themeModel.themes.firstIndex(of: selectedTheme) { + return NSColor(themeModel.themes[index].terminal.background.swiftColor) + } + return .windowBackgroundColor + } } diff --git a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift index b713ed70e..cf7388edf 100644 --- a/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift +++ b/CodeEdit/Features/UtilityArea/ViewModels/UtilityAreaViewModel.swift @@ -12,6 +12,8 @@ import SwiftUI /// A model class to host and manage data for the Utility area. class UtilityAreaViewModel: ObservableObject { + @Published var selectedTab: UtilityAreaTab? = .terminal + @Published var terminals: [UtilityAreaTerminal] = [] @Published var selectedTerminals: Set = [] diff --git a/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift b/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift index bd897f902..97bb62445 100644 --- a/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift +++ b/CodeEdit/Features/UtilityArea/Views/UtilityAreaView.swift @@ -21,12 +21,10 @@ struct UtilityAreaView: View { @StateObject private var themeModel: ThemeModel = .shared - @State var selection: UtilityAreaTab? = .terminal - var body: some View { VStack(spacing: 0) { - if let selection { - selection + if let selectedTab = utilityAreaViewModel.selectedTab { + selectedTab } else { Text("Tab not found") .frame(maxWidth: .infinity, maxHeight: .infinity) @@ -34,7 +32,11 @@ struct UtilityAreaView: View { } .safeAreaInset(edge: .leading, spacing: 0) { HStack(spacing: 0) { - AreaTabBar(items: $utilityAreaViewModel.tabItems, selection: $selection, position: .side) + AreaTabBar( + items: $utilityAreaViewModel.tabItems, + selection: $utilityAreaViewModel.selectedTab, + position: .side + ) Divider() .overlay(Color(nsColor: colorScheme == .dark ? .black : .clear)) }