// // VerticalTabBarView.swift // Zyquo Atlas // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // The left-vertical tab sidebar (Arc-style), used when the layout's // tabPosition is .left. Same TabManager, a stacked column of tabs with a // new-tab button at the top. Themed live by ThemeEngine. // import SwiftUI struct VerticalTabBarView: View { @ObservedObject var tabManager: TabManager let onNewTab: () -> Void @EnvironmentObject private var themeEngine: ThemeEngine private var theme: AtlasTheme { themeEngine.theme } var body: some View { VStack(alignment: .leading, spacing: themeEngine.layout.rowSpacing) { HStack { Text("Zyquo Atlas") .font(themeEngine.layout.font(12, weight: .semibold)) .foregroundStyle(theme.textSecondary) Spacer() Button(action: onNewTab) { Image(systemName: "plus") .font(.system(size: 11, weight: .bold)) .frame(width: 22, height: 22) .contentShape(Rectangle()) } .buttonStyle(.plain) .foregroundStyle(theme.textSecondary) } .padding(.bottom, ZyquoSpacing.xxs) ScrollView(showsIndicators: false) { VStack(spacing: themeEngine.layout.rowSpacing) { ForEach(tabManager.tabs) { tab in VerticalTabRow( tab: tab, isActive: tab.id == tabManager.activeTabID, onSelect: { tabManager.selectTab(tab.id) }, onClose: { tabManager.closeTab(tab.id) } ) } } } Spacer() } .padding(ZyquoSpacing.xs) .frame(width: themeEngine.layout.verticalTabWidth) .background(theme.surfaceSecondary) .overlay(alignment: .trailing) { Rectangle().fill(theme.border).frame(width: ZyquoMetrics.hairline) } } } private struct VerticalTabRow: View { @ObservedObject var tab: Tab let isActive: Bool let onSelect: () -> Void let onClose: () -> Void @EnvironmentObject private var themeEngine: ThemeEngine private var theme: AtlasTheme { themeEngine.theme } @State private var hovering = false var body: some View { HStack(spacing: ZyquoSpacing.xs) { if tab.isLoading { ProgressView().controlSize(.mini).scaleEffect(0.6).frame(width: 14) } else { Image(systemName: "globe").font(.system(size: 10)) .foregroundStyle(theme.textTertiary).frame(width: 14) } Text(tab.title) .font(themeEngine.layout.font(12.5)) .lineLimit(1) .foregroundStyle(isActive ? theme.textPrimary : theme.textSecondary) Spacer(minLength: 0) if hovering || isActive { Button(action: onClose) { Image(systemName: "xmark").font(.system(size: 9, weight: .bold)) .frame(width: 16, height: 16).contentShape(Rectangle()) } .buttonStyle(.plain) .foregroundStyle(theme.textTertiary) } } .padding(.horizontal, ZyquoSpacing.xs) .frame(height: 30) .background( RoundedRectangle(cornerRadius: ZyquoRadius.small) .fill(isActive ? theme.accentSubtle : (hovering ? theme.surface : .clear)) ) .contentShape(Rectangle()) .onTapGesture(perform: onSelect) .onHover { hovering = $0 } } }