import SwiftUI struct TerminalSettingsView: View { @ObservedObject private var settingsController = AppSettingsController.shared @State private var sizePresets: [TerminalSizePreset] = [] var body: some View { Form { Section("Font") { HStack { Text("Font size") Slider(value: settingsController.binding(\.terminal.fontSize), in: 8...28, step: 1) Text("\(Int(settingsController.settings.terminal.fontSize))pt") .monospacedDigit() .frame(width: 50) } } Section("Colors") { Picker("Theme", selection: settingsController.binding(\.terminal.themeRawValue)) { ForEach(TerminalTheme.allCases) { terminalTheme in Text(terminalTheme.label).tag(terminalTheme.rawValue) } } Text(settingsController.settings.terminal.theme.detail) .font(.caption) .foregroundStyle(.secondary) Text("Applies to normal terminal text and the ANSI palette used by tools like `ls`.") .font(.caption) .foregroundStyle(.secondary) } Section("Shell") { TextField("Shell path (empty = $SHELL)", text: settingsController.binding(\.terminal.shellPath)) .textFieldStyle(.roundedBorder) Text("Leave empty to use your default shell ($SHELL or /bin/zsh).") .font(.caption) .foregroundStyle(.secondary) } Section("Size Presets") { ForEach($sizePresets) { $preset in TerminalSizePresetEditor( preset: $preset, currentOpenWidth: settingsController.settings.display.openWidth, currentOpenHeight: settingsController.settings.display.openHeight, onDelete: { deletePreset(id: preset.id) }, onApply: { applyPreset(preset) } ) } HStack { Button("Add Preset") { sizePresets.append( TerminalSizePreset( name: "Preset \(sizePresets.count + 1)", width: settingsController.settings.display.openWidth, height: settingsController.settings.display.openHeight, hotkey: TerminalSizePresetStore.suggestedHotkey(for: sizePresets) ) ) } Button("Reset Presets") { sizePresets = TerminalSizePresetStore.loadDefaults() } } Text("Size preset hotkeys are active when the notch is open. Default presets use ⌘⇧1, ⌘⇧2, and ⌘⇧3.") .font(.caption) .foregroundStyle(.secondary) } } .formStyle(.grouped) .onAppear { synchronizePresetsFromSettings() } .onChange(of: settingsController.settings.terminal.sizePresetsJSON) { _, _ in synchronizePresetsFromSettings() } .onChange(of: sizePresets) { _, newValue in let encoded = TerminalSizePresetStore.encodePresets(newValue) guard encoded != settingsController.settings.terminal.sizePresetsJSON else { return } settingsController.update { $0.terminal.sizePresetsJSON = encoded } } } private func deletePreset(id: UUID) { sizePresets.removeAll { $0.id == id } } private func applyPreset(_ preset: TerminalSizePreset) { settingsController.update { $0.display.openWidth = preset.width $0.display.openHeight = preset.height } ScreenManager.shared.applySizePreset(preset) } private func synchronizePresetsFromSettings() { let decoded = TerminalSizePresetStore.decodePresets( from: settingsController.settings.terminal.sizePresetsJSON ) ?? TerminalSizePresetStore.loadDefaults() guard decoded != sizePresets else { return } sizePresets = decoded } } private struct TerminalSizePresetEditor: View { @Binding var preset: TerminalSizePreset let currentOpenWidth: Double let currentOpenHeight: Double let onDelete: () -> Void let onApply: () -> Void var body: some View { VStack(alignment: .leading, spacing: 10) { HStack { TextField("Preset name", text: $preset.name) .textFieldStyle(.roundedBorder) Button(role: .destructive, action: onDelete) { Image(systemName: "trash") } .buttonStyle(.borderless) } HStack { Text("Width") TextField("Width", value: $preset.width, format: .number.precision(.fractionLength(0))) .textFieldStyle(.roundedBorder) .frame(width: 90) Text("Height") TextField("Height", value: $preset.height, format: .number.precision(.fractionLength(0))) .textFieldStyle(.roundedBorder) .frame(width: 90) Spacer() Button("Use Current Size") { preset.width = currentOpenWidth preset.height = currentOpenHeight } Button("Apply", action: onApply) } OptionalHotkeyRecorderView(label: "Hotkey", binding: $preset.hotkey) } .padding(.vertical, 4) } }