fix(talk): align sessions and chat UI

This commit is contained in:
Peter Steinberger
2025-12-30 06:47:19 +01:00
parent afbd18e8df
commit 7612a83fa2
13 changed files with 181 additions and 60 deletions

View File

@@ -137,9 +137,10 @@ private struct ChatBubbleShape: InsettableShape {
struct ChatMessageBubble: View {
let message: ClawdisChatMessage
let style: ClawdisChatView.Style
let userAccent: Color?
var body: some View {
ChatMessageBody(message: self.message, isUser: self.isUser, style: self.style)
ChatMessageBody(message: self.message, isUser: self.isUser, style: self.style, userAccent: self.userAccent)
.frame(maxWidth: ChatUIConstants.bubbleMaxWidth, alignment: self.isUser ? .trailing : .leading)
.frame(maxWidth: .infinity, alignment: self.isUser ? .trailing : .leading)
.padding(.horizontal, 2)
@@ -153,6 +154,7 @@ private struct ChatMessageBody: View {
let message: ClawdisChatMessage
let isUser: Bool
let style: ClawdisChatView.Style
let userAccent: Color?
var body: some View {
let text = self.primaryText
@@ -287,7 +289,7 @@ private struct ChatMessageBody: View {
private var bubbleFillColor: Color {
if self.isUser {
return ClawdisChatTheme.userBubble
return self.userAccent ?? ClawdisChatTheme.userBubble
}
if self.style == .onboarding {
return ClawdisChatTheme.onboardingAssistantBubble

View File

@@ -101,11 +101,7 @@ enum ClawdisChatTheme {
}
static var userBubble: Color {
#if os(macOS)
Color(nsColor: .systemBlue)
#else
Color(uiColor: .systemBlue)
#endif
Color(red: 127 / 255.0, green: 184 / 255.0, blue: 212 / 255.0)
}
static var assistantBubble: Color {

View File

@@ -13,6 +13,7 @@ public struct ClawdisChatView: View {
@State private var hasPerformedInitialScroll = false
private let showsSessionSwitcher: Bool
private let style: Style
private let userAccent: Color?
private enum Layout {
#if os(macOS)
@@ -37,11 +38,13 @@ public struct ClawdisChatView: View {
public init(
viewModel: ClawdisChatViewModel,
showsSessionSwitcher: Bool = false,
style: Style = .standard)
style: Style = .standard,
userAccent: Color? = nil)
{
self._viewModel = State(initialValue: viewModel)
self.showsSessionSwitcher = showsSessionSwitcher
self.style = style
self.userAccent = userAccent
}
public var body: some View {
@@ -74,7 +77,7 @@ public struct ClawdisChatView: View {
ScrollView {
LazyVStack(spacing: Layout.messageSpacing) {
ForEach(self.visibleMessages) { msg in
ChatMessageBubble(message: msg, style: self.style)
ChatMessageBubble(message: msg, style: self.style, userAccent: self.userAccent)
.frame(
maxWidth: .infinity,
alignment: msg.role.lowercased() == "user" ? .trailing : .leading)

View File

@@ -150,9 +150,36 @@ public final class ClawdisChatViewModel {
}
private static func decodeMessages(_ raw: [AnyCodable]) -> [ClawdisChatMessage] {
raw.compactMap { item in
let decoded = raw.compactMap { item in
(try? ChatPayloadDecoding.decode(item, as: ClawdisChatMessage.self))
}
return Self.dedupeMessages(decoded)
}
private static func dedupeMessages(_ messages: [ClawdisChatMessage]) -> [ClawdisChatMessage] {
var result: [ClawdisChatMessage] = []
result.reserveCapacity(messages.count)
var seen = Set<String>()
for message in messages {
guard let key = Self.dedupeKey(for: message) else {
result.append(message)
continue
}
if seen.contains(key) { continue }
seen.insert(key)
result.append(message)
}
return result
}
private static func dedupeKey(for message: ClawdisChatMessage) -> String? {
guard let timestamp = message.timestamp else { return nil }
let text = message.content.compactMap { $0.text }.joined(separator: "\n")
.trimmingCharacters(in: .whitespacesAndNewlines)
guard !text.isEmpty else { return nil }
return "\(message.role)|\(timestamp)|\(text)"
}
private func performSend() async {