feat: polish scenarios and stabilize keyboard layout height

Add preset-driven polish scenarios (Settings, onboarding, ScenarioChip)
with ScenarioPrompt and style directives; drive keyboard height from
content (240pt) and use viewIsAppearing encapsulated-height offset for
smoother keyboard switches; remove redundant StatusBadge and hide system
dictation via hasDictationKey.
This commit is contained in:
Rocky
2026-06-29 00:08:00 +08:00
parent 4fec0da7f0
commit 1bdb8824ac
32 changed files with 1247 additions and 340 deletions
+21 -35
View File
@@ -62,6 +62,7 @@ struct SettingsView: View {
VStack(spacing: Spacing.md) {
appLanguageSection
engineSection
languageAndPolishSection
// v0.2.1: hide provider/api card when the
// local engine is active regardless of the
// cloud-polish toggle. Local mode is
@@ -73,13 +74,9 @@ struct SettingsView: View {
providerSection
apiSection
}
languageAndModelsSection
if config.engineMode == "local" {
localEngineSettingsSection
}
if config.engineMode == "cloud" {
systemPromptLinkSection
}
if presentation == .tab {
footerLinks
}
@@ -124,19 +121,12 @@ struct SettingsView: View {
EnginePickerSection(config: config)
}
// MARK: - Language & on-device models
// MARK: - Language & polish
private var languageAndModelsSection: some View {
private var languageAndPolishSection: some View {
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
sectionHeader("settings.language.title")
sectionHeader("settings.languageAndPolish.title")
VStack(spacing: 0) {
// v0.2.1: language tab reorder ASR locale ("")
// now sits above the cloud-polish toggle / local models
// block so the row that maps to microphone input comes
// first, the row that maps to post-processing comes
// second. Translation moved into the local-engine
// group (see `localEngineSettingsSection`) so the local
// engine reads as one cohesive card on its own.
LocalePickerRow(
locales: effectiveLocales,
selection: Binding(
@@ -144,6 +134,23 @@ struct SettingsView: View {
set: { config.localeId = $0 }
)
)
if config.isPolishScenarioRowVisible {
Divider().background(palette.divider)
ScenarioPickerRow(config: config, isVisible: true)
if config.engineMode == "cloud", config.isTranslationRowVisible {
Divider().background(palette.divider)
TranslationPickerRow(config: config, isVisible: true)
}
if config.isCustomPolishScenario {
Divider().background(palette.divider)
NavigationLink {
SystemPromptSettingsView(config: config)
} label: {
footerNavigationRow(title: "settings.systemPrompt.edit")
}
.buttonStyle(.plain)
}
}
}
.background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
.overlay(
@@ -246,27 +253,6 @@ struct SettingsView: View {
dynamicLocales = entries
}
// MARK: - System prompt (cloud only)
private var systemPromptLinkSection: some View {
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
sectionHeader("settings.systemPrompt.title")
VStack(spacing: 0) {
NavigationLink {
SystemPromptSettingsView(config: config)
} label: {
footerNavigationRow(title: "settings.systemPrompt.edit")
}
.buttonStyle(.plain)
}
.background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
.stroke(palette.divider, lineWidth: 0.5)
)
}
}
// MARK: - Footer links (tab settings only)
private var footerLinks: some View {