spb/zyquo-atlas Public License
The AI-native macOS web browser — every surface, intelligent.
Swift 75.2%
JavaScript 22%
Shell 2%
Makefile 0.9%
1//2// VerticalTabBarView.swift3// Zyquo Atlas4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// The left-vertical tab sidebar (Arc-style), used when the layout's9// tabPosition is .left. Same TabManager, a stacked column of tabs with a10// new-tab button at the top. Themed live by ThemeEngine.11//1213import SwiftUI1415struct VerticalTabBarView: View {16 @ObservedObject var tabManager: TabManager17 let onNewTab: () -> Void18 @EnvironmentObject private var themeEngine: ThemeEngine19 private var theme: AtlasTheme { themeEngine.theme }2021 var body: some View {22 VStack(alignment: .leading, spacing: themeEngine.layout.rowSpacing) {23 HStack {24 Text("Zyquo Atlas")25 .font(themeEngine.layout.font(12, weight: .semibold))26 .foregroundStyle(theme.textSecondary)27 Spacer()28 Button(action: onNewTab) {29 Image(systemName: "plus")30 .font(.system(size: 11, weight: .bold))31 .frame(width: 22, height: 22)32 .contentShape(Rectangle())33 }34 .buttonStyle(.plain)35 .foregroundStyle(theme.textSecondary)36 }37 .padding(.bottom, ZyquoSpacing.xxs)3839 ScrollView(showsIndicators: false) {40 VStack(spacing: themeEngine.layout.rowSpacing) {41 ForEach(tabManager.tabs) { tab in42 VerticalTabRow(43 tab: tab,44 isActive: tab.id == tabManager.activeTabID,45 onSelect: { tabManager.selectTab(tab.id) },46 onClose: { tabManager.closeTab(tab.id) }47 )48 }49 }50 }51 Spacer()52 }53 .padding(ZyquoSpacing.xs)54 .frame(width: themeEngine.layout.verticalTabWidth)55 .background(theme.surfaceSecondary)56 .overlay(alignment: .trailing) {57 Rectangle().fill(theme.border).frame(width: ZyquoMetrics.hairline)58 }59 }60}6162private struct VerticalTabRow: View {63 @ObservedObject var tab: Tab64 let isActive: Bool65 let onSelect: () -> Void66 let onClose: () -> Void67 @EnvironmentObject private var themeEngine: ThemeEngine68 private var theme: AtlasTheme { themeEngine.theme }69 @State private var hovering = false7071 var body: some View {72 HStack(spacing: ZyquoSpacing.xs) {73 if tab.isLoading {74 ProgressView().controlSize(.mini).scaleEffect(0.6).frame(width: 14)75 } else {76 Image(systemName: "globe").font(.system(size: 10))77 .foregroundStyle(theme.textTertiary).frame(width: 14)78 }79 Text(tab.title)80 .font(themeEngine.layout.font(12.5))81 .lineLimit(1)82 .foregroundStyle(isActive ? theme.textPrimary : theme.textSecondary)83 Spacer(minLength: 0)84 if hovering || isActive {85 Button(action: onClose) {86 Image(systemName: "xmark").font(.system(size: 9, weight: .bold))87 .frame(width: 16, height: 16).contentShape(Rectangle())88 }89 .buttonStyle(.plain)90 .foregroundStyle(theme.textTertiary)91 }92 }93 .padding(.horizontal, ZyquoSpacing.xs)94 .frame(height: 30)95 .background(96 RoundedRectangle(cornerRadius: ZyquoRadius.small)97 .fill(isActive ? theme.accentSubtle : (hovering ? theme.surface : .clear))98 )99 .contentShape(Rectangle())100 .onTapGesture(perform: onSelect)101 .onHover { hovering = $0 }102 }103}104