diff --git a/CHANGELOG.md b/CHANGELOG.md index a974f0f57..c4073193b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Status: unreleased. - **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed). ### Fixes +- macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee. - Gateway: suppress AbortError and transient network errors in unhandled rejections. (#2451) Thanks @Glucksberg. - TTS: keep /tts status replies on text-only commands and avoid duplicate block-stream audio. (#2451) Thanks @Glucksberg. - Security: pin npm overrides to keep tar@7.5.4 for install toolchains. diff --git a/apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatView.swift b/apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatView.swift index 44399a3e6..99b32779f 100644 --- a/apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatView.swift +++ b/apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatView.swift @@ -13,6 +13,7 @@ public struct ClawdbotChatView: View { @State private var showSessions = false @State private var hasPerformedInitialScroll = false @State private var isPinnedToBottom = true + @State private var lastUserMessageID: UUID? private let showsSessionSwitcher: Bool private let style: Style private let markdownVariant: ChatMarkdownVariant @@ -132,8 +133,28 @@ public struct ClawdbotChatView: View { self.hasPerformedInitialScroll = false self.isPinnedToBottom = true } + .onChange(of: self.viewModel.isSending) { _, isSending in + // Scroll to bottom when user sends a message, even if scrolled up. + guard isSending, self.hasPerformedInitialScroll else { return } + self.isPinnedToBottom = true + withAnimation(.snappy(duration: 0.22)) { + self.scrollPosition = self.scrollerBottomID + } + } .onChange(of: self.viewModel.messages.count) { _, _ in - guard self.hasPerformedInitialScroll, self.isPinnedToBottom else { return } + guard self.hasPerformedInitialScroll else { return } + if let lastMessage = self.viewModel.messages.last, + lastMessage.role.lowercased() == "user", + lastMessage.id != self.lastUserMessageID { + self.lastUserMessageID = lastMessage.id + self.isPinnedToBottom = true + withAnimation(.snappy(duration: 0.22)) { + self.scrollPosition = self.scrollerBottomID + } + return + } + + guard self.isPinnedToBottom else { return } withAnimation(.snappy(duration: 0.22)) { self.scrollPosition = self.scrollerBottomID } diff --git a/docs/docs.json b/docs/docs.json index 01a338a18..f4f6258a9 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -956,6 +956,7 @@ "gateway/doctor", "gateway/logging", "gateway/security", + "security/formal-verification", "gateway/sandbox-vs-tool-policy-vs-elevated", "gateway/sandboxing", "gateway/troubleshooting", diff --git a/docs/gateway/security.md b/docs/gateway/security.md index ca532aae0..f83ea3eb6 100644 --- a/docs/gateway/security.md +++ b/docs/gateway/security.md @@ -7,6 +7,8 @@ read_when: ## Quick check: `clawdbot security audit` +See also: [Formal Verification (Security Models)](/security/formal-verification/) + Run this regularly (especially after changing config or exposing network surfaces): ```bash diff --git a/docs/security/formal-verification.md b/docs/security/formal-verification.md index e2704e7b3..6028b8291 100644 --- a/docs/security/formal-verification.md +++ b/docs/security/formal-verification.md @@ -1,7 +1,7 @@ --- title: Formal Verification (Security Models) summary: Machine-checked security models for Clawdbot’s highest-risk paths. -status: draft +permalink: /security/formal-verification/ --- # Formal Verification (Security Models)