feat(app): add flow diagnostics and refine interface
Improve failure recovery visibility, strengthen account-session handling, and make the cross-platform interface more consistent.
This commit is contained in:
@@ -371,7 +371,9 @@ struct TypingRootView: View {
|
||||
}
|
||||
|
||||
private var expandChevronFill: Color {
|
||||
colorScheme == .dark ? Color(white: 0.30) : .white
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkInputFill
|
||||
: OSGColor.fixedLightContent
|
||||
}
|
||||
|
||||
// MARK: - Editing toolbar (iPad)
|
||||
@@ -423,7 +425,9 @@ struct TypingRootView: View {
|
||||
}
|
||||
|
||||
private var editingToolbarButtonFill: Color {
|
||||
colorScheme == .dark ? Color(white: 0.30) : .white
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkInputFill
|
||||
: OSGColor.fixedLightContent
|
||||
}
|
||||
|
||||
/// UIKit-recycled labels — no SwiftUI Button per candidate.
|
||||
@@ -773,7 +777,9 @@ struct TypingRootView: View {
|
||||
}
|
||||
|
||||
private var selectedCandidateFill: Color {
|
||||
colorScheme == .dark ? Color(white: 0.36) : .white
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkInputFillRaised
|
||||
: OSGColor.fixedLightContent
|
||||
}
|
||||
|
||||
private var returnKeyFill: Color {
|
||||
@@ -786,7 +792,9 @@ struct TypingRootView: View {
|
||||
|
||||
private var returnKeyBorder: Color {
|
||||
if state.returnKeyRole.usesActionFill {
|
||||
return Color.black.opacity(colorScheme == .dark ? 0.10 : 0.08)
|
||||
return colorScheme == .dark
|
||||
? OSGColor.keyboardActionBorderDark
|
||||
: OSGColor.keyboardActionBorderLight
|
||||
}
|
||||
return palette.divider
|
||||
}
|
||||
@@ -799,7 +807,7 @@ struct TypingRootView: View {
|
||||
}
|
||||
|
||||
private var returnKeyTextColor: Color {
|
||||
state.returnKeyRole.usesActionFill ? .white : keyTextColor
|
||||
state.returnKeyRole.usesActionFill ? OSGColor.fixedLightContent : keyTextColor
|
||||
}
|
||||
|
||||
/// The send key stays recognizable in both appearances without becoming neon.
|
||||
|
||||
@@ -188,7 +188,7 @@ struct AIKeyboardView: View {
|
||||
Button(action: state.confirmPendingAIAnswer) {
|
||||
Image(systemName: "plus")
|
||||
.font(.system(size: 20, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
.frame(width: Layout.fieldActionWidth, height: 44)
|
||||
.background(palette.accent, in: Capsule())
|
||||
}
|
||||
@@ -709,13 +709,13 @@ struct AIKeyboardView: View {
|
||||
if state.assistantInsertionSucceeded {
|
||||
Image(systemName: "checkmark")
|
||||
.font(.system(size: 27, weight: .bold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
} else {
|
||||
switch state.aiSession.phase {
|
||||
case .listening:
|
||||
waveform(color: .white)
|
||||
waveform(color: OSGColor.fixedLightContent)
|
||||
case .preparing, .recognizing, .generating:
|
||||
ProgressView().tint(.white)
|
||||
ProgressView().tint(OSGColor.fixedLightContent)
|
||||
case .inactive, .idle, .ready, .awaitingSend, .inserted, .sent, .failed:
|
||||
ordinaryMicrophoneContent
|
||||
}
|
||||
@@ -726,17 +726,17 @@ struct AIKeyboardView: View {
|
||||
private var ordinaryMicrophoneContent: some View {
|
||||
switch state.phase {
|
||||
case .recording:
|
||||
waveform(color: Color(red: 1, green: 0.78, blue: 0.78))
|
||||
waveform(color: OSGColor.recordingWaveform)
|
||||
case .requestingPermissions, .processing:
|
||||
ProgressView().tint(.white)
|
||||
ProgressView().tint(OSGColor.fixedLightContent)
|
||||
case .error:
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.font(.system(size: 25, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
case .idle, .denied:
|
||||
Image(systemName: "mic.fill")
|
||||
.font(.system(size: 28, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
.symbolEffect(.breathe, isActive: micIsHoldingForAI)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ struct ClipboardEnableGuideView: View {
|
||||
Button(action: onOpenSettings) {
|
||||
ExtL10n.text("keyboard.clipboard.guide.cta")
|
||||
.font(.system(size: 15, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(palette.accent, in: Capsule())
|
||||
@@ -222,6 +222,7 @@ struct ClipboardHistoryPanelView: View {
|
||||
}
|
||||
.buttonStyle(.glass)
|
||||
.buttonBorderShape(.capsule)
|
||||
.tint(palette.textPrimary)
|
||||
|
||||
Button {
|
||||
// Dismiss the popup before publishing an empty history
|
||||
@@ -231,12 +232,13 @@ struct ClipboardHistoryPanelView: View {
|
||||
} label: {
|
||||
ExtL10n.text("keyboard.clipboard.clear.confirm")
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.foregroundStyle(palette.background)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 36)
|
||||
}
|
||||
.buttonStyle(.glassProminent)
|
||||
.buttonBorderShape(.capsule)
|
||||
.tint(palette.accent)
|
||||
.tint(palette.textPrimary)
|
||||
}
|
||||
}
|
||||
.padding(16)
|
||||
|
||||
@@ -43,7 +43,11 @@ struct KeyboardBrandLogo: View {
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.scaledToFit()
|
||||
.foregroundStyle(colorScheme == .dark ? Color.white : Color.black)
|
||||
.foregroundStyle(
|
||||
colorScheme == .dark
|
||||
? OSGColor.fixedLightContent
|
||||
: OSGColor.fixedDarkContent
|
||||
)
|
||||
.frame(
|
||||
width: KeyboardTopBarMetrics.logoWidth,
|
||||
height: KeyboardTopBarMetrics.logoHeight
|
||||
@@ -222,13 +226,17 @@ struct KeyboardTopControls: View {
|
||||
}
|
||||
|
||||
private var pressedFill: Color {
|
||||
colorScheme == .dark ? Color(white: 0.22) : Color(white: 0.84)
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkControlFill
|
||||
: OSGColor.keyboardLightKeyPressed
|
||||
}
|
||||
|
||||
/// 分段轨道底色与选中键面(NativeKeyboardKeyColors.fill)拉开明度;
|
||||
/// 浅色模式叠加半透明黑色,在不同宿主键盘底色上维持可见对比。
|
||||
private var tabTrackFill: Color {
|
||||
colorScheme == .dark ? Color(white: 0.12) : Color.black.opacity(0.12)
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkControlBorder
|
||||
: OSGColor.keyboardLightControlBorder
|
||||
}
|
||||
|
||||
private func isSelected(_ tab: KeyboardInputTab) -> Bool {
|
||||
|
||||
@@ -123,7 +123,7 @@ struct LastInputEditView: View {
|
||||
Color.clear
|
||||
if case .listening = state.editSession {
|
||||
Capsule()
|
||||
.stroke(Color.white.opacity(0.28), lineWidth: 1.5)
|
||||
.stroke(OSGColor.recordListeningRing, lineWidth: 1.5)
|
||||
.scaleEffect(1 + min(max(state.level, 0), 1) * 0.08)
|
||||
.animation(Motion.soft, value: state.level)
|
||||
}
|
||||
@@ -161,16 +161,16 @@ struct LastInputEditView: View {
|
||||
private var primaryIcon: some View {
|
||||
switch state.editSession {
|
||||
case .preparing, .processing, .applying, .appending:
|
||||
ProgressView().tint(.white)
|
||||
ProgressView().tint(OSGColor.fixedLightContent)
|
||||
case .review:
|
||||
Image(systemName: "checkmark")
|
||||
.font(.system(size: 21, weight: .bold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
case .listening:
|
||||
WaveformView(
|
||||
level: state.level,
|
||||
barCount: 7,
|
||||
color: .white,
|
||||
color: OSGColor.fixedLightContent,
|
||||
active: true
|
||||
)
|
||||
.frame(width: 35, height: 22)
|
||||
@@ -178,7 +178,7 @@ struct LastInputEditView: View {
|
||||
default:
|
||||
Image(systemName: "mic.fill")
|
||||
.font(.system(size: 21, weight: .semibold))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(OSGColor.fixedLightContent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,32 +3,37 @@
|
||||
//
|
||||
// Shared native-like key surface used by voice and typing action rows.
|
||||
|
||||
import OSGKeyboardShared
|
||||
import SwiftUI
|
||||
|
||||
enum NativeKeyboardKeyColors {
|
||||
static func fill(for colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? Color(white: 0.32) : .white
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkKeyFill
|
||||
: OSGColor.fixedLightContent
|
||||
}
|
||||
|
||||
static func pressedFill(for colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? Color(white: 0.23) : Color(white: 0.84)
|
||||
colorScheme == .dark
|
||||
? OSGColor.keyboardDarkKeyPressed
|
||||
: OSGColor.keyboardLightKeyPressed
|
||||
}
|
||||
|
||||
static func text(for colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? .white : Color(red: 0.06, green: 0.06, blue: 0.08)
|
||||
colorScheme == .dark ? OSGColor.fixedLightContent : OSGColor.keyboardLightText
|
||||
}
|
||||
|
||||
/// Adaptive brand green: brighter in dark mode and deeper in light mode.
|
||||
static func sendFill(for colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark
|
||||
? Color(red: 0.286, green: 0.725, blue: 0.416)
|
||||
: Color(red: 0.196, green: 0.549, blue: 0.298)
|
||||
? OSGColor.keyboardDarkSend
|
||||
: OSGColor.keyboardLightSend
|
||||
}
|
||||
|
||||
static func sendPressedFill(for colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark
|
||||
? Color(red: 0.227, green: 0.627, blue: 0.353)
|
||||
: Color(red: 0.157, green: 0.447, blue: 0.247)
|
||||
? OSGColor.keyboardDarkSendPressed
|
||||
: OSGColor.keyboardLightSendPressed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@ private struct ToolbarKeySurface<Content: View>: View {
|
||||
case .standard:
|
||||
return palette.divider
|
||||
case .send:
|
||||
return Color.black.opacity(colorScheme == .dark ? 0.10 : 0.08)
|
||||
return colorScheme == .dark
|
||||
? OSGColor.keyboardActionBorderDark
|
||||
: OSGColor.keyboardActionBorderLight
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,7 +342,7 @@ struct RectangularToolbarButton: View {
|
||||
}
|
||||
|
||||
private var buttonForeground: Color {
|
||||
isSend ? .white : NativeKeyboardKeyColors.text(for: colorScheme)
|
||||
isSend ? OSGColor.fixedLightContent : NativeKeyboardKeyColors.text(for: colorScheme)
|
||||
}
|
||||
|
||||
// 按下即响、即震、即执行,与系统键盘 / 打字面保持一致(Button 默认松手才触发)。
|
||||
|
||||
Reference in New Issue
Block a user