spb/zyquo-router Public MIT
One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).
Swift 95.7%
Python 2.3%
Shell 1.2%
Makefile 0.9%
1//2// MainWindowView.swift3// Zyquo Router4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// App shell: 240pt translucent left navigator (sections, footer settings9// gear, permanent status pill with Start/Stop) + detail area.10//1112import SwiftUI1314struct MainWindowView: View {15 @EnvironmentObject private var appearance: AppearanceStore16 // Stored so the ⌘1–6 menu commands can drive it from the Commands scene.17 @AppStorage("selectedSection") private var sectionRaw = AppSection.dashboard.rawValue1819 private var section: Binding<AppSection> {20 Binding(21 get: { AppSection(rawValue: sectionRaw) ?? .dashboard },22 set: { sectionRaw = $0.rawValue }23 )24 }2526 @State private var paletteShown = false2728 var body: some View {29 HStack(spacing: 0) {30 Navigator(section: section)31 .frame(width: ZyquoMetrics.navigatorWidth)32 Rectangle()33 .fill(ZyquoColor.border)34 .frame(width: ZyquoMetrics.hairline)35 detail36 .frame(maxWidth: .infinity, maxHeight: .infinity)37 .background(ZyquoColor.background)38 }39 .frame(40 minWidth: ZyquoMetrics.windowMinWidth,41 minHeight: ZyquoMetrics.windowMinHeight42 )43 .overlay(alignment: .top) {44 if paletteShown {45 CommandPalette(isPresented: $paletteShown)46 .padding(.top, 90)47 .transition(.opacity.combined(with: .scale(scale: 0.98, anchor: .top)))48 }49 }50 .background(51 Button("") {52 withAnimation(ZyquoMotion.state) { paletteShown.toggle() }53 }54 .keyboardShortcut("k", modifiers: .command)55 .hidden()56 )57 .preferredColorScheme(appearance.themeMode.colorScheme)58 }5960 @ViewBuilder61 private var detail: some View {62 switch section.wrappedValue {63 case .dashboard: DashboardView()64 case .models: ModelsView()65 case .requests: RequestsView()66 case .keys: KeysView()67 case .playground: PlaygroundView()68 case .docs: DocsView()69 }70 }71}7273// MARK: - Navigator7475private struct Navigator: View {76 @Binding var section: AppSection7778 var body: some View {79 VStack(alignment: .leading, spacing: 0) {80 // Wordmark81 HStack(spacing: ZyquoSpacing.xs) {82 RouterZGlyph(size: 20)83 Text("Zyquo Router")84 .font(ZyquoFont.heading)85 .foregroundStyle(ZyquoColor.textPrimary)86 }87 .padding(.horizontal, ZyquoSpacing.md)88 .padding(.top, ZyquoSpacing.xl)89 .padding(.bottom, ZyquoSpacing.lg)9091 ForEach(AppSection.allCases) { item in92 NavigatorRow(item: item, selected: section == item) {93 withAnimation(ZyquoMotion.state) { section = item }94 }95 }9697 Spacer()9899 StatusPill()100 .padding(.horizontal, ZyquoSpacing.md)101 .padding(.bottom, ZyquoSpacing.sm)102103 HStack {104 Button {105 SettingsOpener.open()106 } label: {107 Image(systemName: "gearshape")108 .font(.system(size: 13, weight: .medium))109 .foregroundStyle(ZyquoColor.textSecondary)110 }111 .buttonStyle(.plain)112 .help("Settings")113 Spacer()114 }115 .padding(.horizontal, ZyquoSpacing.md)116 .padding(.bottom, ZyquoSpacing.md)117 }118 .background(NavigatorMaterial())119 }120}121122private struct NavigatorRow: View {123 let item: AppSection124 let selected: Bool125 let action: () -> Void126 @State private var hovering = false127128 var body: some View {129 Button(action: action) {130 HStack(spacing: ZyquoSpacing.xs) {131 Image(systemName: item.systemImage)132 .font(.system(size: 13, weight: .medium))133 .frame(width: 18)134 Text(item.rawValue)135 .font(ZyquoFont.bodyEmphasis())136 Spacer()137 }138 .foregroundStyle(selected ? ZyquoColor.accent : ZyquoColor.textSecondary)139 .padding(.horizontal, ZyquoSpacing.sm)140 .padding(.vertical, 7)141 .background(142 RoundedRectangle(cornerRadius: ZyquoRadius.small, style: .continuous)143 .fill(selected ? ZyquoColor.accentSubtle : (hovering ? ZyquoColor.surfaceSecondary : .clear))144 )145 }146 .buttonStyle(.plain)147 .padding(.horizontal, ZyquoSpacing.xs)148 .onHover { hover in149 withAnimation(ZyquoMotion.hover) { hovering = hover }150 }151 }152}153154/// Translucent sidebar material behind the navigator.155private struct NavigatorMaterial: NSViewRepresentable {156 func makeNSView(context: Context) -> NSVisualEffectView {157 let view = NSVisualEffectView()158 view.material = .sidebar159 view.blendingMode = .behindWindow160 view.state = .followsWindowActiveState161 return view162 }163164 func updateNSView(_ nsView: NSVisualEffectView, context: Context) {}165}166167/// Mini Z-with-signal-dot wordmark glyph (Phase 5 derives the full icon).168struct RouterZGlyph: View {169 let size: CGFloat170171 var body: some View {172 ZStack(alignment: .bottomTrailing) {173 Text("Z")174 .font(.system(size: size, weight: .heavy, design: .rounded))175 .foregroundStyle(ZyquoColor.textPrimary)176 Circle()177 .fill(ZyquoColor.accent)178 .frame(width: size * 0.28, height: size * 0.28)179 .offset(x: size * 0.18, y: 0)180 }181 }182}183