import SwiftUI struct MainView: View { @EnvironmentObject private var model: DatabaseModel @State private var showingEditor = false @State private var editing = ConnectionProfile() @State private var editingPassword = "" var body: some View { NavigationSplitView { navigator } detail: { editorPanel } .sheet(isPresented: $showingEditor) { ConnectionEditor(profile: $editing, password: $editingPassword) { model.save(editing, password: editingPassword) showingEditor = false } } } private var navigator: some View { List(selection: $model.selectedID) { Section("MYSQL CONNECTIONS") { ForEach(model.profiles) { profile in Label(profile.name, systemImage: "server.rack") .tag(profile.id) .contextMenu { Button("Edit Connection") { edit(profile) } } } } Section("SCHEMAS") { ForEach(model.databases, id: \.self) { db in Button { model.selectedDatabase = db } label: { Label(db, systemImage: "cylinder.split.1x2") } .buttonStyle(.plain) } } } .listStyle(.sidebar) .navigationTitle("Navigator") .toolbar { Button { editing = ConnectionProfile(name: "New Connection") editingPassword = "" showingEditor = true } label: { Image(systemName: "plus") } .help("New connection") Button { model.loadDatabases() } label: { Image(systemName: "arrow.clockwise") } .help("Connect / refresh schemas") } } private var editorPanel: some View { VStack(spacing: 0) { editorToolbar Divider() TextEditor(text: $model.sql) .font(.system(size: 14, design: .monospaced)) .frame(minHeight: 190) .padding(8) Divider() resultsPanel Divider() statusBar } .navigationTitle("Workbench Replacement") } private var editorToolbar: some View { HStack { Label("SQL Editor", systemImage: "chevron.left.forwardslash.chevron.right") .font(.headline) Spacer() Button("Edit Connection") { if let profile = model.selected { edit(profile) } } Button("Execute") { model.runSQL() } .keyboardShortcut(.return, modifiers: [.command]) .disabled(model.isRunning) } .padding(12) } private var resultsPanel: some View { VStack(spacing: 0) { HStack { Text("Result Grid").font(.headline) Spacer() Text(model.result.message).foregroundStyle(.secondary) } .padding(12) if model.result.columns.isEmpty { ContentUnavailableView("No Results", systemImage: "tablecells", description: Text("Run a query to display results.")) } else { resultsGrid } } } private var resultsGrid: some View { ScrollView([.horizontal, .vertical]) { Grid(alignment: .leading, horizontalSpacing: 14, verticalSpacing: 8) { GridRow { ForEach(model.result.columns, id: \.self) { name in Text(name) .fontWeight(.semibold) .frame(minWidth: 130, alignment: .leading) } } ForEach(model.result.rows.indices, id: \.self) { rowIndex in resultRow(model.result.rows[rowIndex]) } } .padding(12) } } private func resultRow(_ row: [String]) -> some View { GridRow { ForEach(model.result.columns.indices, id: \.self) { index in Text(index < row.count ? row[index] : "") .font(.system(.body, design: .monospaced)) .frame(minWidth: 130, alignment: .leading) } } } private var statusBar: some View { HStack { Circle() .fill(model.status == "Error" ? Color.red : Color.green) .frame(width: 8, height: 8) Text(model.status).lineLimit(1) Spacer() Text(model.selected?.name ?? "No connection") } .font(.caption) .padding(9) } private func edit(_ profile: ConnectionProfile) { editing = profile editingPassword = PasswordStore.read(profile.id) showingEditor = true } } struct ConnectionEditor: View { @Binding var profile: ConnectionProfile @Binding var password: String let save: () -> Void @Environment(\.dismiss) private var dismiss var body: some View { VStack(alignment: .leading, spacing: 14) { Text("Connection Settings").font(.title2.bold()) Form { TextField("Connection Name", text: $profile.name) TextField("Hostname", text: $profile.host) TextField("Port", value: $profile.port, format: .number) TextField("Username", text: $profile.username) SecureField("Password (stored in macOS Keychain)", text: $password) TextField("Default Schema", text: $profile.database) } Text("Built-in MySQL protocol client. This first version supports loopback TCP connections and local SSH tunnels. Remote TLS and full caching_sha2 authentication are not implemented yet.") .font(.caption).foregroundStyle(.secondary) HStack { Spacer() Button("Cancel") { dismiss() } Button("Save") { save() }.buttonStyle(.borderedProminent) } } .padding(24) .frame(width: 520) } }