Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 39 additions & 39 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1679,7 +1679,7 @@ struct ContentView: View {
VerticalTabsSidebar(
updateViewModel: updateViewModel,
fileExplorerState: fileExplorerState,
windowId: windowId, sidebarWidth: sidebarWidth,
windowId: windowId,
onSendFeedback: presentFeedbackComposer,
onToggleSidebar: { sidebarState.toggle() },
onNewTab: {
Expand Down Expand Up @@ -10008,7 +10008,6 @@ struct VerticalTabsSidebar: View {
var updateViewModel: UpdateStateModel
@ObservedObject var fileExplorerState: FileExplorerState
let windowId: UUID
let sidebarWidth: CGFloat
let onSendFeedback: () -> Void
let onToggleSidebar: () -> Void
let onNewTab: () -> Void
Expand Down Expand Up @@ -10378,7 +10377,7 @@ struct VerticalTabsSidebar: View {
let workspaceCount: Int
let canCloseWorkspace: Bool
let workspaceNumberShortcut: StoredShortcut
let tabItemSettings: SidebarTabItemSettingsSnapshot, sidebarWidth: CGFloat
let tabItemSettings: SidebarTabItemSettingsSnapshot
let tabIndexById: [UUID: Int]
let workspaceById: [UUID: Workspace]
let workspaceGroupIdByWorkspaceId: [UUID: UUID?]
Expand Down Expand Up @@ -10440,7 +10439,7 @@ struct VerticalTabsSidebar: View {
workspaceCount: workspaceCount,
canCloseWorkspace: canCloseWorkspace,
workspaceNumberShortcut: workspaceNumberShortcut,
tabItemSettings: tabItemSettings, sidebarWidth: sidebarWidth,
tabItemSettings: tabItemSettings,
tabIndexById: tabIndexById,
workspaceById: workspaceById,
workspaceGroupIdByWorkspaceId: workspaceGroupIdByWorkspaceId,
Expand Down Expand Up @@ -12141,7 +12140,6 @@ struct VerticalTabsSidebar: View {
)
}

let rowSidebarWidth = renderContext.sidebarWidth - (tab.groupId != nil ? SidebarWorkspaceGroupingMetrics.memberIndent : 0)
let row = TabItemView(
tabManager: tabManager,
notificationStore: notificationStore,
Expand All @@ -12156,7 +12154,7 @@ struct VerticalTabsSidebar: View {
accessibilityWorkspaceCount: renderContext.workspaceCount,
unreadCount: liveUnreadCount,
latestNotificationText: liveLatestNotificationText,
rowSpacing: tabRowSpacing, sidebarWidth: rowSidebarWidth,
rowSpacing: tabRowSpacing,
setSelectionToTabs: { selection = .tabs },
selectedTabIds: $selectedTabIds,
lastSidebarSelectionIndex: $lastSidebarSelectionIndex,
Expand Down Expand Up @@ -12954,7 +12952,7 @@ struct TabItemView: View, Equatable {
lhs.accessibilityWorkspaceCount == rhs.accessibilityWorkspaceCount &&
lhs.unreadCount == rhs.unreadCount &&
lhs.latestNotificationText == rhs.latestNotificationText &&
lhs.rowSpacing == rhs.rowSpacing && lhs.sidebarWidth == rhs.sidebarWidth &&
lhs.rowSpacing == rhs.rowSpacing &&
lhs.showsModifierShortcutHints == rhs.showsModifierShortcutHints &&
lhs.contextMenuWorkspaceIds == rhs.contextMenuWorkspaceIds &&
lhs.remoteContextMenuWorkspaceIds == rhs.remoteContextMenuWorkspaceIds &&
Expand All @@ -12981,7 +12979,7 @@ struct TabItemView: View, Equatable {
let accessibilityWorkspaceCount: Int
let unreadCount: Int
let latestNotificationText: String?
let rowSpacing: CGFloat, sidebarWidth: CGFloat
let rowSpacing: CGFloat
let setSelectionToTabs: () -> Void
@Binding var selectedTabIds: Set<UUID>
@Binding var lastSidebarSelectionIndex: Int?
Expand All @@ -12996,9 +12994,9 @@ struct TabItemView: View, Equatable {
let isBeingDragged: Bool
let topDropIndicatorVisible: Bool
let onDragStart: () -> NSItemProvider
/// Factory invoked from `body` with a stable drop-hit height. Closure
/// Factory invoked from `body` with the row's measured `rowHeight`. Closure
/// captures the parent's `dragState`, so TabItemView itself never holds an
/// `@Observable` store reference or layout-driven state (snapshot-boundary rule).
/// `@Observable` store reference (snapshot-boundary rule).
let tabDropDelegateFactory: (CGFloat) -> SidebarTabDropDelegate
let contextMenuWorkspaceIds: [UUID]
let remoteContextMenuWorkspaceIds: [UUID]
Expand All @@ -13018,10 +13016,10 @@ struct TabItemView: View, Equatable {
@State private var observedIsActive: Bool?
@StateObject private var contextMenuState = SidebarTabItemContextMenuState()
@State private var rowInteractionState = SidebarWorkspaceRowInteractionState()
@State private var metadataRowsExpanded = false
@State private var metadataBlocksExpanded = false
@State private var rowHeight: CGFloat = 1
@State private var workspaceFinderDirectoryOpenRequest: WorkspaceFinderDirectoryOpenRequest?

private static let maxWrappedTitleLines = 8
private static let maxDisplayedTitleCharacters = 2048

var isMultiSelected: Bool {
Expand Down Expand Up @@ -13254,6 +13252,18 @@ struct TabItemView: View, Equatable {
}
}

private var rowHeightProbe: some View {
GeometryReader { proxy in
Color.clear
.onAppear {
rowHeight = max(proxy.size.height, 1)
}
.onChange(of: proxy.size.height) { _, newHeight in
rowHeight = max(newHeight, 1)
}
}
}

@ViewBuilder
private var remoteWorkspaceSection: some View {
let workspaceSnapshot = self.workspaceSnapshot
Expand Down Expand Up @@ -13357,20 +13367,11 @@ struct TabItemView: View, Equatable {
: nil
let effectiveSubtitle = latestNotificationSubtitle ?? conversationMessageSubtitle
let detailVisibility = visibleAuxiliaryDetails
let titleLineLimit = settings.wrapsWorkspaceTitles ? SidebarWorkspaceRowDropMetrics.maxWrappedTitleLines : 1
let titleLineLimit = settings.wrapsWorkspaceTitles ? Self.maxWrappedTitleLines : 1
let displayedTitle = workspaceSnapshot.title.sidebarBoundedDisplayString(
maxDisplayedLines: titleLineLimit,
maxDisplayedCharacters: Self.maxDisplayedTitleCharacters
)
let dropTargetHeight = SidebarWorkspaceRowDropMetrics.dropTargetHeight(
snapshot: workspaceSnapshot,
settings: settings,
effectiveSubtitle: effectiveSubtitle,
metadataEntryIsExpanded: metadataRowsExpanded,
metadataBlocksAreExpanded: metadataBlocksExpanded,
sidebarWidth: sidebarWidth, unreadCount: unreadCount,
canCloseWorkspace: canCloseWorkspace
)
let scaledUnreadBadgeSize = 16 * fontScale
let scaledCloseButtonHitSize = max(16, 16 * fontScale)
let scaledCloseButtonWidth = max(
Expand Down Expand Up @@ -13487,7 +13488,6 @@ struct TabItemView: View, Equatable {
if !metadataEntries.isEmpty {
SidebarMetadataRows(
entries: metadataEntries,
isExpanded: $metadataRowsExpanded,
isActive: usesInvertedActiveForeground,
activeForegroundColor: activeSecondaryColor(0.95),
activeSecondaryForegroundColor: activeSecondaryColor(0.65),
Expand All @@ -13499,7 +13499,6 @@ struct TabItemView: View, Equatable {
if !metadataBlocks.isEmpty {
SidebarMetadataMarkdownBlocks(
blocks: metadataBlocks,
isExpanded: $metadataBlocksExpanded,
isActive: usesInvertedActiveForeground,
activeForegroundColor: activeSecondaryColor(0.8),
activeSecondaryForegroundColor: activeSecondaryColor(0.65),
Expand Down Expand Up @@ -13733,6 +13732,7 @@ struct TabItemView: View, Equatable {
)
.shortcutHintVisibilityAnimation(value: showsWorkspaceShortcutHint)
.padding(.horizontal, SidebarWorkspaceListMetrics.rowOuterHorizontalPadding)
.background { rowHeightProbe }
.contentShape(Rectangle())
.opacity(isBeingDragged ? 0.6 : 1)
.overlay {
Expand Down Expand Up @@ -13812,7 +13812,7 @@ struct TabItemView: View, Equatable {
}
.onDrag(onDragStart)
.internalOnlyTabDrag()
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: tabDropDelegateFactory(dropTargetHeight))
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: tabDropDelegateFactory(rowHeight))
.onDrop(of: BonsplitTabDragPayload.dropContentTypes, delegate: SidebarBonsplitTabDropDelegate(
targetWorkspaceId: tab.id,
tabManager: tabManager,
Expand Down Expand Up @@ -14937,7 +14937,7 @@ private struct SidebarWorkspaceDescriptionText: View {
let isActive: Bool
let activeForegroundColor: Color
let fontScale: CGFloat
private static let maxDisplayedLines = SidebarWorkspaceRowDropMetrics.maxDescriptionLines
private static let maxDisplayedLines = 12
private static let maxDisplayedCharacters = 4096

var body: some View {
Expand Down Expand Up @@ -15034,13 +15034,15 @@ private extension String {

private struct SidebarMetadataRows: View {
let entries: [SidebarStatusEntry]
@Binding var isExpanded: Bool
let isActive: Bool
let activeForegroundColor: Color
let activeSecondaryForegroundColor: Color
let fontScale: CGFloat
let onFocus: () -> Void

@State private var isExpanded: Bool = false
private let collapsedEntryLimit = 3

var body: some View {
VStack(alignment: .leading, spacing: 2) {
ForEach(visibleEntries, id: \.key) { entry in
Expand Down Expand Up @@ -15070,10 +15072,8 @@ private struct SidebarMetadataRows: View {
}

private var visibleEntries: [SidebarStatusEntry] {
guard !isExpanded, entries.count > SidebarWorkspaceRowDropMetrics.collapsedMetadataEntryLimit else {
return entries
}
return Array(entries.prefix(SidebarWorkspaceRowDropMetrics.collapsedMetadataEntryLimit))
guard !isExpanded, entries.count > collapsedEntryLimit else { return entries }
return Array(entries.prefix(collapsedEntryLimit))
}

private var helpText: String {
Expand All @@ -15085,7 +15085,7 @@ private struct SidebarMetadataRows: View {
}

private var shouldShowToggle: Bool {
entries.count > SidebarWorkspaceRowDropMetrics.collapsedMetadataEntryLimit
entries.count > collapsedEntryLimit
}
}

Expand Down Expand Up @@ -15190,13 +15190,15 @@ private struct SidebarMetadataEntryRow: View {

private struct SidebarMetadataMarkdownBlocks: View {
let blocks: [SidebarMetadataBlock]
@Binding var isExpanded: Bool
let isActive: Bool
let activeForegroundColor: Color
let activeSecondaryForegroundColor: Color
let fontScale: CGFloat
let onFocus: () -> Void

@State private var isExpanded: Bool = false
private let collapsedBlockLimit = 1

var body: some View {
VStack(alignment: .leading, spacing: 3) {
ForEach(visibleBlocks, id: \.key) { block in
Expand Down Expand Up @@ -15225,14 +15227,12 @@ private struct SidebarMetadataMarkdownBlocks: View {
}

private var visibleBlocks: [SidebarMetadataBlock] {
guard !isExpanded, blocks.count > SidebarWorkspaceRowDropMetrics.collapsedMetadataBlockLimit else {
return blocks
}
return Array(blocks.prefix(SidebarWorkspaceRowDropMetrics.collapsedMetadataBlockLimit))
guard !isExpanded, blocks.count > collapsedBlockLimit else { return blocks }
return Array(blocks.prefix(collapsedBlockLimit))
}

private var shouldShowToggle: Bool {
blocks.count > SidebarWorkspaceRowDropMetrics.collapsedMetadataBlockLimit
blocks.count > collapsedBlockLimit
}
}

Expand All @@ -15242,7 +15242,7 @@ private struct SidebarMetadataMarkdownBlockRow: View {
let activeForegroundColor: Color
let fontScale: CGFloat
let onFocus: () -> Void
private static let maxDisplayedLines = SidebarWorkspaceRowDropMetrics.maxMetadataBlockLines
private static let maxDisplayedLines = 12
private static let maxDisplayedCharacters = 4096

var body: some View {
Expand Down
5 changes: 0 additions & 5 deletions Sources/SidebarWorkspaceGroupHeaderMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,4 @@ struct SidebarWorkspaceGroupHeaderMetrics: Equatable {
var plusFontSize: CGFloat { Self.basePlusFontSize * fontScale }
/// Scaled plus-button frame edge.
var plusFrame: CGFloat { max(Self.basePlusFrame, Self.basePlusFrame * fontScale) }
/// Stable drop-hit height for the group header, without reading SwiftUI layout.
var dropTargetHeight: CGFloat {
let contentHeight = max(chevronFrame, iconFrame, plusFrame, nameFontSize + 4)
return max(24 * fontScale, contentHeight + 10)
}
}
19 changes: 15 additions & 4 deletions Sources/SidebarWorkspaceGroupHeaderView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ struct SidebarWorkspaceGroupHeaderView: View, Equatable {
let isBeingDragged: Bool
let topDropIndicatorVisible: Bool
let onDragStart: () -> NSItemProvider
/// Factory invoked from `body` with a stable drop-hit height. Closure
/// captures the parent's drag state while this LazyVStack row stays free of
/// layout-driven state writes.
let tabDropDelegateFactory: (CGFloat) -> SidebarWorkspaceGroupHeaderDropDelegate
let onToggleCollapsed: () -> Void
let onFocusAnchor: () -> Void
Expand All @@ -89,6 +86,7 @@ struct SidebarWorkspaceGroupHeaderView: View, Equatable {
let onOpenDocs: () -> Void

@State private var rowInteractionState = SidebarWorkspaceRowInteractionState()
@State private var rowHeight: CGFloat = 1

private var metrics: SidebarWorkspaceGroupHeaderMetrics {
SidebarWorkspaceGroupHeaderMetrics(fontScale: fontScale)
Expand All @@ -112,6 +110,18 @@ struct SidebarWorkspaceGroupHeaderView: View, Equatable {
return "\(shortcutModifierSymbol)\(shortcutDigit)"
}

private var rowHeightProbe: some View {
GeometryReader { proxy in
Color.clear
.onAppear {
rowHeight = max(proxy.size.height, 1)
}
.onChange(of: proxy.size.height) { _, newHeight in
rowHeight = max(newHeight, 1)
}
}
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
var body: some View {
HStack(spacing: 4) {
Image(systemName: isCollapsed ? "chevron.right" : "chevron.down")
Expand Down Expand Up @@ -237,6 +247,7 @@ struct SidebarWorkspaceGroupHeaderView: View, Equatable {
offsetY: shortcutHintYOffset
)
.padding(.horizontal, SidebarWorkspaceListMetrics.rowOuterHorizontalPadding)
.background { rowHeightProbe }
.shortcutHintVisibilityAnimation(value: showsShortcutHint)
.opacity(isBeingDragged ? 0.6 : 1)
.overlay(alignment: .top) {
Expand All @@ -251,7 +262,7 @@ struct SidebarWorkspaceGroupHeaderView: View, Equatable {
}
.onDrag(onDragStart)
.internalOnlyTabDrag()
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: tabDropDelegateFactory(metrics.dropTargetHeight))
.onDrop(of: SidebarTabDragPayload.dropContentTypes, delegate: tabDropDelegateFactory(rowHeight))
.contextMenu {
Button(
String(
Expand Down
Loading
Loading