fix(ui): refine talk overlays
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
- macOS Talk Mode: fix audio stop ordering so disabling Talk Mode always stops in-flight playback.
|
||||
- macOS Talk Mode: throttle audio-level updates (avoid per-buffer task creation) to reduce CPU/task churn.
|
||||
- macOS Talk Mode: increase overlay window size so wave rings don’t clip; close button is hover-only and closer to the orb.
|
||||
- Talk Mode: align to the gateway’s main session key and fall back to history polling when chat events drop (prevents stuck “thinking” / missing messages).
|
||||
- Talk Mode: treat history timestamps as seconds or milliseconds to avoid stale assistant picks (macOS/iOS/Android).
|
||||
- Chat UI: dedupe identical history messages to avoid duplicate bubbles.
|
||||
- Chat UI: user bubbles use `ui.seamColor` (fallback to a calmer default blue).
|
||||
- Talk Mode: wait for chat history to surface the assistant reply before starting TTS (macOS/iOS/Android).
|
||||
- iOS Talk Mode: fix chat completion wait to time out even if no events arrive (prevents “Thinking…” hangs).
|
||||
- iOS Talk Mode: keep recognition running during playback to support interrupt-on-speech.
|
||||
|
||||
@@ -62,7 +62,7 @@ fun TalkOrbOverlay(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Canvas(modifier = Modifier.size(240.dp)) {
|
||||
Canvas(modifier = Modifier.size(300.dp)) {
|
||||
val center = this.center
|
||||
val baseRadius = size.minDimension * 0.27f
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ struct TalkOrbOverlay: View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(seam.opacity(0.26), lineWidth: 2)
|
||||
.frame(width: 220, height: 220)
|
||||
.frame(width: 280, height: 280)
|
||||
.scaleEffect(self.pulse ? 1.15 : 0.96)
|
||||
.opacity(self.pulse ? 0.0 : 1.0)
|
||||
.animation(.easeOut(duration: 1.3).repeatForever(autoreverses: false), value: self.pulse)
|
||||
|
||||
Circle()
|
||||
.stroke(seam.opacity(0.18), lineWidth: 2)
|
||||
.frame(width: 220, height: 220)
|
||||
.frame(width: 280, height: 280)
|
||||
.scaleEffect(self.pulse ? 1.45 : 1.02)
|
||||
.opacity(self.pulse ? 0.0 : 0.9)
|
||||
.animation(.easeOut(duration: 1.9).repeatForever(autoreverses: false).delay(0.2), value: self.pulse)
|
||||
@@ -34,8 +34,8 @@ struct TalkOrbOverlay: View {
|
||||
],
|
||||
center: .center,
|
||||
startRadius: 1,
|
||||
endRadius: 92))
|
||||
.frame(width: 136, height: 136)
|
||||
endRadius: 112))
|
||||
.frame(width: 168, height: 168)
|
||||
.overlay(
|
||||
Circle()
|
||||
.stroke(seam.opacity(0.35), lineWidth: 1))
|
||||
|
||||
@@ -7,7 +7,8 @@ import SwiftUI
|
||||
@Observable
|
||||
final class TalkOverlayController {
|
||||
static let shared = TalkOverlayController()
|
||||
static let overlaySize: CGFloat = 320
|
||||
static let overlaySize: CGFloat = 360
|
||||
static let windowInset: CGFloat = 88
|
||||
|
||||
private let logger = Logger(subsystem: "com.steipete.clawdis", category: "talk.overlay")
|
||||
|
||||
@@ -110,8 +111,8 @@ final class TalkOverlayController {
|
||||
let size = NSSize(width: Self.overlaySize, height: Self.overlaySize)
|
||||
let visible = screen.visibleFrame
|
||||
let origin = CGPoint(
|
||||
x: visible.maxX - size.width - self.padding,
|
||||
y: visible.maxY - size.height - self.padding)
|
||||
x: visible.maxX - size.width - self.padding + Self.windowInset,
|
||||
y: visible.maxY - size.height - self.padding + Self.windowInset)
|
||||
return NSRect(origin: origin, size: size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ struct TalkOverlayView: View {
|
||||
level: self.controller.model.level,
|
||||
accent: self.seamColor)
|
||||
.frame(width: 96, height: 96)
|
||||
.padding(.top, 6)
|
||||
.padding(.trailing, 6)
|
||||
.padding(.top, 6 + TalkOverlayController.windowInset)
|
||||
.padding(.trailing, 6 + TalkOverlayController.windowInset)
|
||||
.contentShape(Circle())
|
||||
.onTapGesture {
|
||||
TalkModeController.shared.stopSpeaking(reason: .userTap)
|
||||
@@ -31,7 +31,7 @@ struct TalkOverlayView: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.contentShape(Circle())
|
||||
.offset(x: -7, y: -7)
|
||||
.offset(x: -5, y: -5)
|
||||
.opacity(self.hoveringWindow ? 1 : 0)
|
||||
.animation(.easeOut(duration: 0.12), value: self.hoveringWindow)
|
||||
.allowsHitTesting(self.hoveringWindow)
|
||||
|
||||
Reference in New Issue
Block a user