From b7a20ba3109005807f3e3d096ab603840eb8e504 Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Fri, 24 Oct 2025 10:15:35 -0400 Subject: [PATCH 1/5] Hide favorited/pinned tabs placeholder after use --- ora/Modules/Sidebar/ContainerView.swift | 3 +- ora/Modules/Sidebar/SidebarView.swift | 2 +- ora/Modules/Sidebar/TabList/FavTabsList.swift | 35 +++++++++++++------ .../Sidebar/TabList/PinnedTabsList.swift | 24 +++++++++++-- ora/Services/SectionDropDelegate.swift | 17 +++++++++ 5 files changed, 67 insertions(+), 14 deletions(-) diff --git a/ora/Modules/Sidebar/ContainerView.swift b/ora/Modules/Sidebar/ContainerView.swift index 4791cf04..33fb2035 100644 --- a/ora/Modules/Sidebar/ContainerView.swift +++ b/ora/Modules/Sidebar/ContainerView.swift @@ -15,7 +15,7 @@ struct ContainerView: View { @State private var editingURLString: String = "" var body: some View { - VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: 0) { if toolbarManager.isToolbarHidden, let tab = tabManager.activeTab { SidebarURLDisplay( tab: tab, @@ -73,6 +73,7 @@ struct ContainerView: View { onMoveToContainer: moveTab, containers: containers ) + Divider() } NormalTabsList( diff --git a/ora/Modules/Sidebar/SidebarView.swift b/ora/Modules/Sidebar/SidebarView.swift index c1eb47da..6eca875c 100644 --- a/ora/Modules/Sidebar/SidebarView.swift +++ b/ora/Modules/Sidebar/SidebarView.swift @@ -47,7 +47,7 @@ struct SidebarView: View { } var body: some View { - VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading) { SidebarToolbar() NSPageView( selection: selectedContainerIndex, diff --git a/ora/Modules/Sidebar/TabList/FavTabsList.swift b/ora/Modules/Sidebar/TabList/FavTabsList.swift index 68c095c0..518a2c95 100644 --- a/ora/Modules/Sidebar/TabList/FavTabsList.swift +++ b/ora/Modules/Sidebar/TabList/FavTabsList.swift @@ -2,8 +2,10 @@ import AppKit import SwiftUI struct FavTabsGrid: View { + @AppStorage("ui.sidebar.favorites.sticky") private var sticky: Bool = true @Environment(\.theme) var theme @EnvironmentObject var tabManager: TabManager + @State private var isHoveringOverEmpty: Bool = false let tabs: [Tab] @Binding var draggedItem: UUID? let onDrag: (UUID) -> NSItemProvider @@ -25,18 +27,26 @@ struct FavTabsGrid: View { } var body: some View { - LazyVGrid(columns: adaptiveColumns, spacing: 10) { + let isShowingHidden = tabs.isEmpty && !(sticky || isHoveringOverEmpty) + LazyVGrid(columns: adaptiveColumns, spacing: isShowingHidden ? 0 : 10) { if tabs.isEmpty { - EmptyFavTabItem() - .onDrop( - of: [.text], - delegate: SectionDropDelegate( - items: tabs, - draggedItem: $draggedItem, - targetSection: .fav, - tabManager: tabManager - ) + Group { + if sticky || isHoveringOverEmpty { + EmptyFavTabItem() + } else { + Capsule().frame(height: 3).opacity(0) + } + } + .onDrop( + of: [.text], + delegate: SectionDropDelegate( + items: tabs, + draggedItem: $draggedItem, + targetSection: .fav, + tabManager: tabManager, + isHovering: $isHoveringOverEmpty ) + ) } else { ForEach(tabs) { tab in FavTabItem( @@ -71,5 +81,10 @@ struct FavTabsGrid: View { tabManager: tabManager ) ) + .onChange(of: tabs.count) { _, newTabs in + if newTabs > 0 { + sticky = false + } + } } } diff --git a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift index 63b16ab1..89757493 100644 --- a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift +++ b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift @@ -2,8 +2,10 @@ import SwiftData import SwiftUI struct PinnedTabsList: View { + @AppStorage("ui.sidebar.pinned.sticky") private var sticky: Bool = true let tabs: [Tab] @Binding var draggedItem: UUID? + @State private var isHoveringOverEmpty = false let onDrag: (UUID) -> NSItemProvider let onSelect: (Tab) -> Void let onPinToggle: (Tab) -> Void @@ -15,15 +17,28 @@ struct PinnedTabsList: View { @EnvironmentObject var tabManager: TabManager @Environment(\.theme) var theme + private var spaceName: String? { + if let active = tabManager.activeContainer { + return "\(active.emoji) \(active.name)" + } + return nil + } + var body: some View { VStack(spacing: 8) { - Text("Pinned") + Text(spaceName ?? "Pinned") .font(.callout) .foregroundColor(theme.mutedForeground) .padding(.top, 8) .frame(maxWidth: .infinity, alignment: .leading) if tabs.isEmpty { - EmptyPinnedTabs() + Group { + if sticky || isHoveringOverEmpty { + EmptyPinnedTabs() + } else { + Capsule().frame(height: 3).opacity(0) + } + } } else { ForEach(tabs) { tab in TabItem( @@ -60,5 +75,10 @@ struct PinnedTabsList: View { tabManager: tabManager ) ) + .onChange(of: tabs.count) { _, new in + if new > 0 { + sticky = false + } + } } } diff --git a/ora/Services/SectionDropDelegate.swift b/ora/Services/SectionDropDelegate.swift index e6a70639..50e55e5c 100644 --- a/ora/Services/SectionDropDelegate.swift +++ b/ora/Services/SectionDropDelegate.swift @@ -4,9 +4,24 @@ import SwiftUI struct SectionDropDelegate: DropDelegate { let items: [Tab] @Binding var draggedItem: UUID? + @Binding var isHovering: Bool let targetSection: TabSection let tabManager: TabManager + init( + items: [Tab], + draggedItem: Binding, + targetSection: TabSection, + tabManager: TabManager, + isHovering: Binding? = nil + ) { + self.items = items + self._draggedItem = draggedItem + self.targetSection = targetSection + self.tabManager = tabManager + self._isHovering = isHovering ?? .constant(false) + } + func dropEntered(info: DropInfo) { guard let provider = info.itemProviders(for: [.text]).first else { return } performHapticFeedback(pattern: .alignment) @@ -50,6 +65,8 @@ struct SectionDropDelegate: DropDelegate { } } + func dropExited(info: DropInfo) {} + func dropUpdated(info: DropInfo) -> DropProposal? { DropProposal(operation: .move) } From f0662fcc442b6d320bc2baea2e8601ffe2d75574 Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Fri, 24 Oct 2025 13:02:42 -0400 Subject: [PATCH 2/5] Small fixes to show placeholder --- .../Sidebar/TabList/PinnedTabsList.swift | 3 ++- ora/Services/SectionDropDelegate.swift | 24 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift index 89757493..33607924 100644 --- a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift +++ b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift @@ -72,7 +72,8 @@ struct PinnedTabsList: View { items: tabs, draggedItem: $draggedItem, targetSection: .pinned, - tabManager: tabManager + tabManager: tabManager, + isHovering: $isHoveringOverEmpty ) ) .onChange(of: tabs.count) { _, new in diff --git a/ora/Services/SectionDropDelegate.swift b/ora/Services/SectionDropDelegate.swift index 50e55e5c..e4aa55c5 100644 --- a/ora/Services/SectionDropDelegate.swift +++ b/ora/Services/SectionDropDelegate.swift @@ -23,7 +23,20 @@ struct SectionDropDelegate: DropDelegate { } func dropEntered(info: DropInfo) { - guard let provider = info.itemProviders(for: [.text]).first else { return } + isHovering = true + } + + func dropExited(info: DropInfo) { + isHovering = false + } + + func dropUpdated(info: DropInfo) -> DropProposal? { + DropProposal(operation: .move) + } + + func performDrop(info: DropInfo) -> Bool { + isHovering = false + guard let provider = info.itemProviders(for: [.text]).first else { return false } performHapticFeedback(pattern: .alignment) provider.loadObject(ofClass: NSString.self) { object, _ in @@ -63,15 +76,6 @@ struct SectionDropDelegate: DropDelegate { // } } } - } - - func dropExited(info: DropInfo) {} - - func dropUpdated(info: DropInfo) -> DropProposal? { - DropProposal(operation: .move) - } - - func performDrop(info: DropInfo) -> Bool { draggedItem = nil return true } From 3ef5810344fc30ce205cdb9eac8261dd0637a68f Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Wed, 29 Oct 2025 12:34:08 -0400 Subject: [PATCH 3/5] Moved into sidebar manager --- ora/Modules/Sidebar/TabList/FavTabsList.swift | 10 ++++++---- ora/Modules/Sidebar/TabList/PinnedTabsList.swift | 6 +++--- ora/Services/SidebarManager.swift | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ora/Modules/Sidebar/TabList/FavTabsList.swift b/ora/Modules/Sidebar/TabList/FavTabsList.swift index 518a2c95..4ff88b84 100644 --- a/ora/Modules/Sidebar/TabList/FavTabsList.swift +++ b/ora/Modules/Sidebar/TabList/FavTabsList.swift @@ -2,9 +2,9 @@ import AppKit import SwiftUI struct FavTabsGrid: View { - @AppStorage("ui.sidebar.favorites.sticky") private var sticky: Bool = true @Environment(\.theme) var theme @EnvironmentObject var tabManager: TabManager + @EnvironmentObject var sidebarManager: SidebarManager @State private var isHoveringOverEmpty: Bool = false let tabs: [Tab] @Binding var draggedItem: UUID? @@ -27,11 +27,13 @@ struct FavTabsGrid: View { } var body: some View { - let isShowingHidden = tabs.isEmpty && !(sticky || isHoveringOverEmpty) + let isShowingHidden = tabs.isEmpty && !( + sidebarManager.stickyFavs || isHoveringOverEmpty + ) LazyVGrid(columns: adaptiveColumns, spacing: isShowingHidden ? 0 : 10) { if tabs.isEmpty { Group { - if sticky || isHoveringOverEmpty { + if sidebarManager.stickyFavs || isHoveringOverEmpty { EmptyFavTabItem() } else { Capsule().frame(height: 3).opacity(0) @@ -83,7 +85,7 @@ struct FavTabsGrid: View { ) .onChange(of: tabs.count) { _, newTabs in if newTabs > 0 { - sticky = false + sidebarManager.stickyFavs = false } } } diff --git a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift index 33607924..729d67c4 100644 --- a/ora/Modules/Sidebar/TabList/PinnedTabsList.swift +++ b/ora/Modules/Sidebar/TabList/PinnedTabsList.swift @@ -2,7 +2,6 @@ import SwiftData import SwiftUI struct PinnedTabsList: View { - @AppStorage("ui.sidebar.pinned.sticky") private var sticky: Bool = true let tabs: [Tab] @Binding var draggedItem: UUID? @State private var isHoveringOverEmpty = false @@ -15,6 +14,7 @@ struct PinnedTabsList: View { let onMoveToContainer: (Tab, TabContainer) -> Void let containers: [TabContainer] @EnvironmentObject var tabManager: TabManager + @EnvironmentObject var sidebarManager: SidebarManager @Environment(\.theme) var theme private var spaceName: String? { @@ -33,7 +33,7 @@ struct PinnedTabsList: View { .frame(maxWidth: .infinity, alignment: .leading) if tabs.isEmpty { Group { - if sticky || isHoveringOverEmpty { + if sidebarManager.stickyPinned || isHoveringOverEmpty { EmptyPinnedTabs() } else { Capsule().frame(height: 3).opacity(0) @@ -78,7 +78,7 @@ struct PinnedTabsList: View { ) .onChange(of: tabs.count) { _, new in if new > 0 { - sticky = false + sidebarManager.stickyPinned = false } } } diff --git a/ora/Services/SidebarManager.swift b/ora/Services/SidebarManager.swift index 8ac6eb1e..92e33789 100644 --- a/ora/Services/SidebarManager.swift +++ b/ora/Services/SidebarManager.swift @@ -7,6 +7,8 @@ enum SidebarPosition: String, Hashable { @MainActor class SidebarManager: ObservableObject { + @AppStorage("ui.sidebar.favorites.sticky") var stickyFavs: Bool = true + @AppStorage("ui.sidebar.pinned.sticky") var stickyPinned: Bool = true @AppStorage("ui.sidebar.hidden") var isSidebarHidden: Bool = false @AppStorage("ui.sidebar.position") var sidebarPosition: SidebarPosition = .primary From ac4d49a125783c23009694d892debfcd7fe8f735 Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Wed, 29 Oct 2025 12:56:12 -0400 Subject: [PATCH 4/5] Close button --- ora/Modules/Sidebar/ContainerView.swift | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/ora/Modules/Sidebar/ContainerView.swift b/ora/Modules/Sidebar/ContainerView.swift index 33fb2035..026210cf 100644 --- a/ora/Modules/Sidebar/ContainerView.swift +++ b/ora/Modules/Sidebar/ContainerView.swift @@ -11,6 +11,7 @@ struct ContainerView: View { @EnvironmentObject var privacyMode: PrivacyMode @State var isDragging = false + @State private var isHovered = false @State private var draggedItem: UUID? @State private var editingURLString: String = "" @@ -74,7 +75,26 @@ struct ContainerView: View { containers: containers ) - Divider() + HStack { + ZStack { + Capsule() + .frame(height: 1) + Text("Clear").bold().font(.footnote).opacity(0) + } + if isHovered { + Button("Clear") { + withAnimation { + for tab in container.tabs where tab.type == .normal { + tabManager.closeTab(tab: tab) + } + } + } + .bold() + .font(.footnote) + .buttonStyle(.plain) + } + } + .foregroundStyle(.secondary) } NormalTabsList( tabs: normalTabs, @@ -92,6 +112,11 @@ struct ContainerView: View { } } .modifier(OraWindowDragGesture(isDragging: $isDragging)) + .onHover { hov in + withAnimation { + isHovered = hov + } + } } private var favoriteTabs: [Tab] { From a0f8a34126e090b2295508259703fec7abfcaa8b Mon Sep 17 00:00:00 2001 From: Jack Hogan Date: Sun, 28 Dec 2025 10:44:36 -0500 Subject: [PATCH 5/5] Add clear condition --- ora/Modules/Sidebar/ContainerView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ora/Modules/Sidebar/ContainerView.swift b/ora/Modules/Sidebar/ContainerView.swift index 026210cf..bde74ed1 100644 --- a/ora/Modules/Sidebar/ContainerView.swift +++ b/ora/Modules/Sidebar/ContainerView.swift @@ -81,7 +81,7 @@ struct ContainerView: View { .frame(height: 1) Text("Clear").bold().font(.footnote).opacity(0) } - if isHovered { + if isHovered, !normalTabs.isEmpty { Button("Clear") { withAnimation { for tab in container.tabs where tab.type == .normal {