feat: add streaming cloud ASR, polish routing, and settings card layout
Unify Bailian/Volcengine/OpenAI realtime streaming, ABE polish routing with fun styles, and a shared card-page Settings hierarchy; bump to 1.1 (build 32).
This commit is contained in:
@@ -7,34 +7,37 @@
|
||||
import SwiftUI
|
||||
import OSGKeyboardShared
|
||||
|
||||
struct EnginePickerSection: View {
|
||||
struct EnginePickerSection<ConfigurationRows: View>: View {
|
||||
@Environment(\.themePalette) private var palette: ThemePalette
|
||||
|
||||
@ObservedObject var config: ProviderConfig
|
||||
private let configurationRows: ConfigurationRows
|
||||
|
||||
init(
|
||||
config: ProviderConfig,
|
||||
@ViewBuilder configurationRows: () -> ConfigurationRows
|
||||
) {
|
||||
self.config = config
|
||||
self.configurationRows = configurationRows()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
|
||||
sectionHeader("settings.engine.title")
|
||||
CardSection("settings.engine.title") {
|
||||
VStack(spacing: 0) {
|
||||
engineOptionRow(
|
||||
id: "local",
|
||||
systemIcon: "iphone.badge.checkmark",
|
||||
title: AppL10n.string("settings.engine.local.title"),
|
||||
subtitle: localSubtitle
|
||||
)
|
||||
Divider().background(palette.divider)
|
||||
engineOptionRow(
|
||||
id: "cloud",
|
||||
systemIcon: "wand.and.stars",
|
||||
title: AppL10n.string("settings.engine.cloud.title"),
|
||||
subtitle: AppL10n.string("settings.engine.cloud.subtitle")
|
||||
)
|
||||
configurationRows
|
||||
}
|
||||
.background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
|
||||
.stroke(palette.divider, lineWidth: 0.5)
|
||||
)
|
||||
.surfaceCard()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +47,6 @@ struct EnginePickerSection: View {
|
||||
|
||||
private func engineOptionRow(
|
||||
id: String,
|
||||
assetName: String? = nil,
|
||||
systemIcon: String? = nil,
|
||||
title: String,
|
||||
subtitle: String
|
||||
) -> some View {
|
||||
@@ -55,11 +56,6 @@ struct EnginePickerSection: View {
|
||||
selectEngine(id)
|
||||
} label: {
|
||||
HStack(spacing: Spacing.sm) {
|
||||
engineMark(
|
||||
assetName: assetName,
|
||||
systemIcon: systemIcon,
|
||||
isSelected: isSelected
|
||||
)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.font(TypeStyle.body)
|
||||
@@ -90,33 +86,12 @@ struct EnginePickerSection: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func engineMark(assetName: String?, systemIcon: String?, isSelected: Bool) -> some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(isSelected ? palette.accentMuted : palette.surfaceElevated)
|
||||
.frame(width: 32, height: 32)
|
||||
if let assetName {
|
||||
Image(assetName)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 18, height: 18)
|
||||
.foregroundStyle(isSelected ? palette.accent : palette.textPrimary)
|
||||
} else if let systemIcon {
|
||||
Image(systemName: systemIcon)
|
||||
.font(.system(size: 16, weight: .medium))
|
||||
.foregroundStyle(isSelected ? palette.accent : palette.textSecondary)
|
||||
}
|
||||
}
|
||||
.frame(width: 32, height: 32)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func sectionHeader(_ title: LocalizedStringKey) -> some View {
|
||||
Text(title)
|
||||
.font(TypeStyle.caption2)
|
||||
.foregroundStyle(palette.textSecondary)
|
||||
.textCase(.uppercase)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
extension EnginePickerSection where ConfigurationRows == EmptyView {
|
||||
init(config: ProviderConfig) {
|
||||
self.init(config: config) {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user