// // ParametersEditorView.swift // Zyquo Cloud // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // // Per-conversation generation parameters. Only the controls the selected // model actually supports are shown (ParameterSupport gates each row). // import SwiftUI struct ParametersEditorView: View { @Binding var parameters: ChatParameters let support: ParameterSupport var body: some View { VStack(alignment: .leading, spacing: ZyquoSpacing.sm) { Text("Parameters") .font(ZyquoFont.bodyEmphasis()) .foregroundStyle(ZyquoColor.textPrimary) if support.temperature { optionalSlider( "Temperature", value: $parameters.temperature, range: 0...2, defaultValue: 1, format: "%.2f" ) } if support.topP { optionalSlider( "Top P", value: $parameters.topP, range: 0...1, defaultValue: 1, format: "%.2f" ) } maxTokensRow if support.frequencyPenalty { optionalSlider( "Frequency penalty", value: $parameters.frequencyPenalty, range: -2...2, defaultValue: 0, format: "%.1f" ) } if support.presencePenalty { optionalSlider( "Presence penalty", value: $parameters.presencePenalty, range: -2...2, defaultValue: 0, format: "%.1f" ) } if support.reasoningEffort { reasoningEffortRow } if support.thinkingToggle { Toggle("Extended thinking", isOn: Binding( get: { parameters.thinkingEnabled ?? false }, set: { parameters.thinkingEnabled = $0 } )) .font(ZyquoFont.body(size: 12.5)) } } .padding(ZyquoSpacing.md) .frame(width: 300) } private var maxTokensRow: some View { HStack { Text("Max tokens") .font(ZyquoFont.body(size: 12.5)) Spacer() TextField( "default", value: Binding( get: { parameters.maxTokens }, set: { parameters.maxTokens = $0 } ), format: .number ) .textFieldStyle(.roundedBorder) .frame(width: 90) .font(ZyquoFont.body(size: 12)) } } private var reasoningEffortRow: some View { HStack { Text("Reasoning effort") .font(ZyquoFont.body(size: 12.5)) Spacer() Picker("", selection: Binding( get: { parameters.reasoningEffort ?? "default" }, set: { parameters.reasoningEffort = $0 == "default" ? nil : $0 } )) { Text("Default").tag("default") Text("Low").tag("low") Text("Medium").tag("medium") Text("High").tag("high") } .pickerStyle(.menu) .frame(width: 110) } } /// A slider with an enable checkbox: unchecked = provider default (omitted). private func optionalSlider( _ title: String, value: Binding, range: ClosedRange, defaultValue: Double, format: String ) -> some View { VStack(alignment: .leading, spacing: 2) { HStack { Toggle(isOn: Binding( get: { value.wrappedValue != nil }, set: { value.wrappedValue = $0 ? defaultValue : nil } )) { Text(title).font(ZyquoFont.body(size: 12.5)) } .toggleStyle(.checkbox) Spacer() Text(value.wrappedValue.map { String(format: format, $0) } ?? "default") .font(ZyquoFont.caption) .foregroundStyle(ZyquoColor.textTertiary) .monospacedDigit() } if let current = value.wrappedValue { Slider( value: Binding(get: { current }, set: { value.wrappedValue = $0 }), in: range ) .controlSize(.small) } } } }