diff --git a/Info.plist b/Info.plist
index b8af175..14247e9 100644
--- a/Info.plist
+++ b/Info.plist
@@ -13,9 +13,9 @@
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleVersion
- 10
+ 11
CFBundleShortVersionString
- 1.5.13
+ 1.5.14
LSMinimumSystemVersion
$(MACOSX_DEPLOYMENT_TARGET)
LSApplicationCategoryType
diff --git a/Sources/Fluid/UI/AISettings/AISettingsScreens.swift b/Sources/Fluid/UI/AISettings/AISettingsScreens.swift
index d351d8d..8c1845e 100644
--- a/Sources/Fluid/UI/AISettings/AISettingsScreens.swift
+++ b/Sources/Fluid/UI/AISettings/AISettingsScreens.swift
@@ -16,16 +16,13 @@ struct VoiceEngineSettingsScreen: View {
}
var body: some View {
- ScrollView(.vertical, showsIndicators: false) {
- VStack(alignment: .leading, spacing: 14) {
- VoiceEngineSettingsView(
- viewModel: self.viewModel,
- settings: self.viewModel.settings,
- theme: self.theme
- )
- }
- .padding(14)
- }
+ VoiceEngineSettingsView(
+ viewModel: self.viewModel,
+ settings: self.viewModel.settings,
+ theme: self.theme
+ )
+ .padding(14)
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
}
diff --git a/Sources/Fluid/UI/AISettingsView+SpeechRecognition.swift b/Sources/Fluid/UI/AISettingsView+SpeechRecognition.swift
index e7322d7..b339c35 100644
--- a/Sources/Fluid/UI/AISettingsView+SpeechRecognition.swift
+++ b/Sources/Fluid/UI/AISettingsView+SpeechRecognition.swift
@@ -45,120 +45,125 @@ extension VoiceEngineSettingsView {
.shadow(color: self.theme.metrics.cardShadow.color.opacity(self.theme.metrics.cardShadow.opacity), radius: self.theme.metrics.cardShadow.radius, x: self.theme.metrics.cardShadow.x, y: self.theme.metrics.cardShadow.y)
)
- HStack(spacing: 6) {
- Image(systemName: "info.circle")
- .font(.caption2)
- .foregroundStyle(.secondary)
- Text("Click a row to preview. Press Activate to load the model.")
- .font(.caption2)
- .foregroundStyle(.secondary)
- Spacer()
- Menu {
- ForEach(SpeechProviderFilter.allCases) { option in
- Button(option.rawValue) {
- self.viewModel.providerFilter = option
- }
- }
- } label: {
+ ScrollView(.vertical, showsIndicators: false) {
+ VStack(alignment: .leading, spacing: 14) {
HStack(spacing: 6) {
- Image(systemName: "line.3.horizontal.decrease.circle")
- .font(.caption)
- Text("Filter: \(self.viewModel.providerFilter.rawValue)")
- .font(.caption)
- .fontWeight(.semibold)
- }
- .foregroundStyle(.primary)
- .padding(.horizontal, 10)
- .padding(.vertical, 6)
- .background(
- RoundedRectangle(cornerRadius: 9)
- .fill(self.theme.palette.cardBackground.opacity(0.8))
- .overlay(
+ Image(systemName: "info.circle")
+ .font(.caption2)
+ .foregroundStyle(.secondary)
+ Text("Click a row to preview. Press Activate to load the model.")
+ .font(.caption2)
+ .foregroundStyle(.secondary)
+ Spacer()
+ Menu {
+ ForEach(SpeechProviderFilter.allCases) { option in
+ Button(option.rawValue) {
+ self.viewModel.providerFilter = option
+ }
+ }
+ } label: {
+ HStack(spacing: 6) {
+ Image(systemName: "line.3.horizontal.decrease.circle")
+ .font(.caption)
+ Text("Filter: \(self.viewModel.providerFilter.rawValue)")
+ .font(.caption)
+ .fontWeight(.semibold)
+ }
+ .foregroundStyle(.primary)
+ .padding(.horizontal, 10)
+ .padding(.vertical, 6)
+ .background(
RoundedRectangle(cornerRadius: 9)
- .stroke(self.theme.palette.cardBorder.opacity(0.5), lineWidth: 1)
+ .fill(self.theme.palette.cardBackground.opacity(0.8))
+ .overlay(
+ RoundedRectangle(cornerRadius: 9)
+ .stroke(self.theme.palette.cardBorder.opacity(0.5), lineWidth: 1)
+ )
)
- )
- }
- Menu {
- ForEach(ModelSortOption.allCases) { option in
- Button(option.rawValue) {
- self.viewModel.modelSortOption = option
}
- }
- } label: {
- HStack(spacing: 6) {
- Text("Sort by: \(self.viewModel.modelSortOption.rawValue)")
- .font(.caption)
- .fontWeight(.semibold)
- }
- .foregroundStyle(.primary)
- .padding(.horizontal, 10)
- .padding(.vertical, 6)
- .background(
- RoundedRectangle(cornerRadius: 9)
- .fill(self.theme.palette.cardBackground.opacity(0.8))
- .overlay(
+ Menu {
+ ForEach(ModelSortOption.allCases) { option in
+ Button(option.rawValue) {
+ self.viewModel.modelSortOption = option
+ }
+ }
+ } label: {
+ HStack(spacing: 6) {
+ Text("Sort by: \(self.viewModel.modelSortOption.rawValue)")
+ .font(.caption)
+ .fontWeight(.semibold)
+ }
+ .foregroundStyle(.primary)
+ .padding(.horizontal, 10)
+ .padding(.vertical, 6)
+ .background(
RoundedRectangle(cornerRadius: 9)
- .stroke(self.theme.palette.cardBorder.opacity(0.5), lineWidth: 1)
+ .fill(self.theme.palette.cardBackground.opacity(0.8))
+ .overlay(
+ RoundedRectangle(cornerRadius: 9)
+ .stroke(self.theme.palette.cardBorder.opacity(0.5), lineWidth: 1)
+ )
)
- )
- }
- }
-
- // Active + Other models list
- VStack(alignment: .leading, spacing: 10) {
- if let activeModel {
- VStack(alignment: .leading, spacing: 6) {
- Text("Active Model")
- .font(.callout)
- .fontWeight(.semibold)
- .foregroundStyle(.secondary)
- self.speechModelCard(for: activeModel)
- }
- } else {
- VStack(alignment: .leading, spacing: 6) {
- Text("Active Model")
- .font(.callout)
- .fontWeight(.semibold)
- .foregroundStyle(.secondary)
- Label("No active model yet. Download and activate one below.", systemImage: "arrow.down.circle")
- .font(.caption)
- .foregroundStyle(.secondary)
+ }
}
- }
- Divider().padding(.vertical, 2)
+ // Active + Other models list
+ VStack(alignment: .leading, spacing: 10) {
+ if let activeModel {
+ VStack(alignment: .leading, spacing: 6) {
+ Text("Active Model")
+ .font(.callout)
+ .fontWeight(.semibold)
+ .foregroundStyle(.secondary)
+ self.speechModelCard(for: activeModel)
+ }
+ } else {
+ VStack(alignment: .leading, spacing: 6) {
+ Text("Active Model")
+ .font(.callout)
+ .fontWeight(.semibold)
+ .foregroundStyle(.secondary)
+ Label("No active model yet. Download and activate one below.", systemImage: "arrow.down.circle")
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
+ }
+
+ Divider().padding(.vertical, 2)
- VStack(alignment: .leading, spacing: 6) {
- Text(hasActiveModel ? "Other Models" : "Available Models")
- .font(.callout)
- .fontWeight(.semibold)
- .foregroundStyle(.secondary)
- VStack(spacing: 8) {
- ForEach(otherModels) { model in
- self.speechModelCard(for: model)
+ VStack(alignment: .leading, spacing: 6) {
+ Text(hasActiveModel ? "Other Models" : "Available Models")
+ .font(.callout)
+ .fontWeight(.semibold)
+ .foregroundStyle(.secondary)
+ VStack(spacing: 8) {
+ ForEach(otherModels) { model in
+ self.speechModelCard(for: model)
+ }
+ }
}
}
- }
- }
- .padding(12)
- .background(
- RoundedRectangle(cornerRadius: 12)
- .fill(self.theme.palette.cardBackground.opacity(0.9))
- .overlay(
+ .padding(12)
+ .background(
RoundedRectangle(cornerRadius: 12)
- .stroke(self.theme.palette.cardBorder.opacity(0.3), lineWidth: 1)
+ .fill(self.theme.palette.cardBackground.opacity(0.9))
+ .overlay(
+ RoundedRectangle(cornerRadius: 12)
+ .stroke(self.theme.palette.cardBorder.opacity(0.3), lineWidth: 1)
+ )
+ .shadow(color: self.theme.metrics.cardShadow.color.opacity(self.theme.metrics.cardShadow.opacity), radius: self.theme.metrics.cardShadow.radius, x: self.theme.metrics.cardShadow.x, y: self.theme.metrics.cardShadow.y)
)
- .shadow(color: self.theme.metrics.cardShadow.color.opacity(self.theme.metrics.cardShadow.opacity), radius: self.theme.metrics.cardShadow.radius, x: self.theme.metrics.cardShadow.x, y: self.theme.metrics.cardShadow.y)
- )
- Divider().padding(.vertical, 4)
+ Divider().padding(.vertical, 4)
- // Filler Words Section
- self.fillerWordsSection
+ // Filler Words Section
+ self.fillerWordsSection
+ }
+ }
}
.padding(14)
}
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
/// Stats panel showing speed/accuracy bars that animate when model changes