feat: keyboard bottom-row layout, handedness preference, and screen wake lock
Rework the action cluster to a mic-above-bottom-row layout, add left/right handedness setting that swaps delete and return, and keep the screen awake during Flow recording sessions.
This commit is contained in:
@@ -140,6 +140,7 @@ final class FlowSessionManager: ObservableObject {
|
||||
FlowSessionBridge.writeHeartbeat()
|
||||
FlowSessionDarwin.postSessionChanged()
|
||||
isActive = true
|
||||
ScreenWakeLock.acquire()
|
||||
if let expires = FlowSessionBridge.sessionExpiresAt() {
|
||||
sessionExpiresAt = Date(timeIntervalSince1970: expires)
|
||||
}
|
||||
@@ -187,6 +188,7 @@ final class FlowSessionManager: ObservableObject {
|
||||
|
||||
capture.stop()
|
||||
endBackgroundKeepAlive()
|
||||
ScreenWakeLock.release()
|
||||
sessionASR = nil
|
||||
FlowSessionBridge.markSessionInactive()
|
||||
FlowSessionDarwin.postSessionChanged()
|
||||
@@ -321,6 +323,7 @@ final class FlowSessionManager: ObservableObject {
|
||||
FlowSessionBridge.markSessionActive(duration: duration)
|
||||
FlowSessionDarwin.postSessionChanged()
|
||||
isActive = true
|
||||
ScreenWakeLock.acquire()
|
||||
sessionExpiresAt = Date().addingTimeInterval(duration)
|
||||
|
||||
startHeartbeat()
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// ScreenWakeLock.swift
|
||||
// OSGKeyboard · Main App
|
||||
//
|
||||
// Reference-counted idle-timer disable for Flow session ownership.
|
||||
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
enum ScreenWakeLock {
|
||||
private static var holdCount = 0
|
||||
|
||||
static func acquire() {
|
||||
holdCount += 1
|
||||
if holdCount == 1 {
|
||||
UIApplication.shared.isIdleTimerDisabled = true
|
||||
}
|
||||
}
|
||||
|
||||
static func release() {
|
||||
guard holdCount > 0 else { return }
|
||||
holdCount -= 1
|
||||
if holdCount == 0 {
|
||||
UIApplication.shared.isIdleTimerDisabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ struct SettingsView: View {
|
||||
localEngineSettingsSection
|
||||
}
|
||||
if presentation == .tab {
|
||||
preferencesSection
|
||||
footerLinks
|
||||
}
|
||||
}
|
||||
@@ -253,6 +254,27 @@ struct SettingsView: View {
|
||||
dynamicLocales = entries
|
||||
}
|
||||
|
||||
// MARK: - Preferences (tab settings only)
|
||||
|
||||
private var preferencesSection: some View {
|
||||
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
|
||||
sectionHeader("settings.preferences.title")
|
||||
VStack(spacing: 0) {
|
||||
HandednessPickerRow(
|
||||
selection: Binding(
|
||||
get: { config.handednessPreference },
|
||||
set: { config.handednessPreference = $0 }
|
||||
)
|
||||
)
|
||||
}
|
||||
.background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.xl, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: Radius.xl, style: .continuous)
|
||||
.stroke(palette.divider, lineWidth: 0.5)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Footer links (tab settings only)
|
||||
|
||||
private var footerLinks: some View {
|
||||
@@ -366,6 +388,31 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Handedness picker row
|
||||
|
||||
private struct HandednessPickerRow: View {
|
||||
@Binding var selection: HandednessPreference
|
||||
|
||||
private var options: [(id: String, label: String)] {
|
||||
HandednessPreference.allCases.map { preference in
|
||||
(preference.rawValue, AppL10n.string(preference.labelKey))
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
PickerRow(
|
||||
title: AppL10n.string("settings.handedness.title"),
|
||||
options: options,
|
||||
selection: Binding(
|
||||
get: { selection.rawValue },
|
||||
set: { newValue in
|
||||
selection = HandednessPreference(rawValue: newValue) ?? .left
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Picker row (generic)
|
||||
|
||||
private struct PickerRow: View {
|
||||
|
||||
@@ -135,6 +135,10 @@
|
||||
"settings.systemPrompt.edit" = "Edit system prompt";
|
||||
"settings.systemPrompt.hint" = "Used by the cloud LLM when polishing your dictation. Local engine skips this step.";
|
||||
"settings.about.title" = "About";
|
||||
"settings.preferences.title" = "Preferences";
|
||||
"settings.handedness.title" = "Handedness";
|
||||
"settings.handedness.left" = "Left hand";
|
||||
"settings.handedness.right" = "Right hand";
|
||||
"settings.systemPrompt.reset" = "Reset";
|
||||
"settings.asrLocale" = "ASR locale";
|
||||
"settings.engineBadge.ios26" = "SpeechAnalyzer — always on-device";
|
||||
|
||||
@@ -135,6 +135,10 @@
|
||||
"settings.systemPrompt.edit" = "编辑系统提示";
|
||||
"settings.systemPrompt.hint" = "云端润色时使用。本地识别模式不会用到此提示词。";
|
||||
"settings.about.title" = "关于";
|
||||
"settings.preferences.title" = "偏好设置";
|
||||
"settings.handedness.title" = "握持偏好";
|
||||
"settings.handedness.left" = "左手";
|
||||
"settings.handedness.right" = "右手";
|
||||
"settings.systemPrompt.reset" = "重置";
|
||||
"settings.asrLocale" = "识别语言";
|
||||
"settings.engineBadge.ios26" = "SpeechAnalyzer — 始终端侧";
|
||||
|
||||
Reference in New Issue
Block a user