Update Sidebar
This commit is contained in:
parent
f559d19390
commit
692bd5f9d4
@ -3,7 +3,7 @@ import SwiftData
|
|||||||
import KeyboardShortcuts
|
import KeyboardShortcuts
|
||||||
|
|
||||||
// ViewType enum with all cases
|
// ViewType enum with all cases
|
||||||
enum ViewType: String, CaseIterable {
|
enum ViewType: String, CaseIterable, Identifiable {
|
||||||
case metrics = "Dashboard"
|
case metrics = "Dashboard"
|
||||||
case transcribeAudio = "Transcribe Audio"
|
case transcribeAudio = "Transcribe Audio"
|
||||||
case history = "History"
|
case history = "History"
|
||||||
@ -16,6 +16,8 @@ enum ViewType: String, CaseIterable {
|
|||||||
case settings = "Settings"
|
case settings = "Settings"
|
||||||
case license = "VoiceInk Pro"
|
case license = "VoiceInk Pro"
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
|
||||||
var icon: String {
|
var icon: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .metrics: return "gauge.medium"
|
case .metrics: return "gauge.medium"
|
||||||
@ -51,13 +53,15 @@ struct VisualEffectView: NSViewRepresentable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DynamicSidebar: View {
|
struct ContentView: View {
|
||||||
@Binding var selectedView: ViewType
|
@Environment(\.modelContext) private var modelContext
|
||||||
@Binding var hoveredView: ViewType?
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
|
@EnvironmentObject private var whisperState: WhisperState
|
||||||
|
@EnvironmentObject private var hotkeyManager: HotkeyManager
|
||||||
@AppStorage("powerModeUIFlag") private var powerModeUIFlag = false
|
@AppStorage("powerModeUIFlag") private var powerModeUIFlag = false
|
||||||
|
@State private var selectedView: ViewType? = .metrics
|
||||||
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
|
||||||
@StateObject private var licenseViewModel = LicenseViewModel()
|
@StateObject private var licenseViewModel = LicenseViewModel()
|
||||||
@Namespace private var buttonAnimation
|
|
||||||
|
|
||||||
private var visibleViewTypes: [ViewType] {
|
private var visibleViewTypes: [ViewType] {
|
||||||
ViewType.allCases.filter { viewType in
|
ViewType.allCases.filter { viewType in
|
||||||
@ -69,7 +73,9 @@ struct DynamicSidebar: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 15) {
|
NavigationSplitView {
|
||||||
|
List(selection: $selectedView) {
|
||||||
|
Section {
|
||||||
// App Header
|
// App Header
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
if let appIcon = NSImage(named: "AppIcon") {
|
if let appIcon = NSImage(named: "AppIcon") {
|
||||||
@ -95,103 +101,45 @@ struct DynamicSidebar: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.vertical, 4)
|
||||||
.padding(.vertical, 12)
|
|
||||||
|
|
||||||
// Navigation Items
|
|
||||||
ForEach(visibleViewTypes, id: \.self) { viewType in
|
|
||||||
DynamicSidebarButton(
|
|
||||||
title: viewType.rawValue,
|
|
||||||
systemImage: viewType.icon,
|
|
||||||
isSelected: selectedView == viewType,
|
|
||||||
isHovered: hoveredView == viewType,
|
|
||||||
namespace: buttonAnimation
|
|
||||||
) {
|
|
||||||
selectedView = viewType
|
|
||||||
}
|
|
||||||
.onHover { isHovered in
|
|
||||||
hoveredView = isHovered ? viewType : nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
ForEach(visibleViewTypes) { viewType in
|
||||||
}
|
Section {
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
NavigationLink(value: viewType) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct DynamicSidebarButton: View {
|
|
||||||
let title: String
|
|
||||||
let systemImage: String
|
|
||||||
let isSelected: Bool
|
|
||||||
let isHovered: Bool
|
|
||||||
let namespace: Namespace.ID
|
|
||||||
let action: () -> Void
|
|
||||||
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button(action: action) {
|
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Image(systemName: systemImage)
|
Image(systemName: viewType.icon)
|
||||||
.font(.system(size: 18, weight: .medium))
|
.font(.system(size: 18, weight: .medium))
|
||||||
.frame(width: 24, height: 24)
|
.frame(width: 24, height: 24)
|
||||||
|
|
||||||
Text(title)
|
Text(viewType.rawValue)
|
||||||
.font(.system(size: 14, weight: .medium))
|
.font(.system(size: 14, weight: .medium))
|
||||||
.lineLimit(1)
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.foregroundColor(isSelected ? .white : (isHovered ? .accentColor : .primary))
|
.padding(.vertical, 8)
|
||||||
.frame(height: 40)
|
.padding(.horizontal, 2)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
}
|
||||||
.padding(.leading, 16)
|
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||||
.background(
|
.listRowSeparator(.hidden)
|
||||||
ZStack {
|
|
||||||
if isSelected {
|
|
||||||
RoundedRectangle(cornerRadius: 12)
|
|
||||||
.fill(Color.accentColor)
|
|
||||||
.shadow(color: Color.accentColor.opacity(0.5), radius: 5, x: 0, y: 2)
|
|
||||||
} else if isHovered {
|
|
||||||
RoundedRectangle(cornerRadius: 12)
|
|
||||||
.fill(colorScheme == .dark ? Color.white.opacity(0.1) : Color.black.opacity(0.05))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
.padding(.horizontal, 8)
|
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.listStyle(.sidebar)
|
||||||
}
|
.navigationTitle("VoiceInk")
|
||||||
}
|
.navigationSplitViewColumnWidth(210)
|
||||||
|
|
||||||
struct ContentView: View {
|
|
||||||
@Environment(\.modelContext) private var modelContext
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
@EnvironmentObject private var whisperState: WhisperState
|
|
||||||
@EnvironmentObject private var hotkeyManager: HotkeyManager
|
|
||||||
@AppStorage("powerModeUIFlag") private var powerModeUIFlag = false
|
|
||||||
@State private var selectedView: ViewType = .metrics
|
|
||||||
@State private var hoveredView: ViewType?
|
|
||||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0"
|
|
||||||
@StateObject private var licenseViewModel = LicenseViewModel()
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
NavigationSplitView {
|
|
||||||
DynamicSidebar(
|
|
||||||
selectedView: $selectedView,
|
|
||||||
hoveredView: $hoveredView
|
|
||||||
)
|
|
||||||
.frame(width: 200)
|
|
||||||
.navigationSplitViewColumnWidth(200)
|
|
||||||
} detail: {
|
} detail: {
|
||||||
detailView
|
if let selectedView = selectedView {
|
||||||
|
detailView(for: selectedView)
|
||||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.toolbar(.hidden, for: .automatic)
|
.navigationTitle(selectedView.rawValue)
|
||||||
.navigationTitle("")
|
} else {
|
||||||
|
Text("Select a view")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.navigationSplitViewStyle(.balanced)
|
.navigationSplitViewStyle(.balanced)
|
||||||
.frame(minWidth: 940, minHeight: 730)
|
.frame(minWidth: 940, minHeight: 730)
|
||||||
// inside ContentView body:
|
|
||||||
.onReceive(NotificationCenter.default.publisher(for: .navigateToDestination)) { notification in
|
.onReceive(NotificationCenter.default.publisher(for: .navigateToDestination)) { notification in
|
||||||
if let destination = notification.userInfo?["destination"] as? String {
|
if let destination = notification.userInfo?["destination"] as? String {
|
||||||
switch destination {
|
switch destination {
|
||||||
@ -219,8 +167,8 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var detailView: some View {
|
private func detailView(for viewType: ViewType) -> some View {
|
||||||
switch selectedView {
|
switch viewType {
|
||||||
case .metrics:
|
case .metrics:
|
||||||
MetricsView()
|
MetricsView()
|
||||||
case .models:
|
case .models:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user