fix: keep chat pinned on stream
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Models: inherit session model overrides in thread/topic sessions (Telegram topics, Slack/Discord threads). (#1376)
|
- Models: inherit session model overrides in thread/topic sessions (Telegram topics, Slack/Discord threads). (#1376)
|
||||||
- macOS: keep local auto bind loopback-first; only use tailnet when bind=tailnet.
|
- macOS: keep local auto bind loopback-first; only use tailnet when bind=tailnet.
|
||||||
- macOS: include Textual syntax highlighting resources in packaged app to prevent chat crashes. (#1362)
|
- macOS: include Textual syntax highlighting resources in packaged app to prevent chat crashes. (#1362)
|
||||||
|
- macOS: keep chat pinned to bottom during streaming replies. (#1279)
|
||||||
- Cron: cap reminder context history to 10 messages and honor `contextMessages`. (#1103) Thanks @mkbehr.
|
- Cron: cap reminder context history to 10 messages and honor `contextMessages`. (#1103) Thanks @mkbehr.
|
||||||
- Exec approvals: treat main as the default agent + migrate legacy default allowlists. (#1417) Thanks @czekaj.
|
- Exec approvals: treat main as the default agent + migrate legacy default allowlists. (#1417) Thanks @czekaj.
|
||||||
- Exec: avoid defaulting to elevated mode when elevated is not allowed.
|
- Exec: avoid defaulting to elevated mode when elevated is not allowed.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ public struct ClawdbotChatView: View {
|
|||||||
@State private var scrollPosition: UUID?
|
@State private var scrollPosition: UUID?
|
||||||
@State private var showSessions = false
|
@State private var showSessions = false
|
||||||
@State private var hasPerformedInitialScroll = false
|
@State private var hasPerformedInitialScroll = false
|
||||||
|
@State private var isPinnedToBottom = true
|
||||||
private let showsSessionSwitcher: Bool
|
private let showsSessionSwitcher: Bool
|
||||||
private let style: Style
|
private let style: Style
|
||||||
private let markdownVariant: ChatMarkdownVariant
|
private let markdownVariant: ChatMarkdownVariant
|
||||||
@@ -87,36 +88,28 @@ public struct ClawdbotChatView: View {
|
|||||||
private var messageList: some View {
|
private var messageList: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
LazyVStack(spacing: Layout.messageSpacing) {
|
||||||
|
self.messageListRows
|
||||||
|
|
||||||
|
Color.clear
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
VStack(spacing: 0) {
|
|
||||||
LazyVStack(spacing: Layout.messageSpacing) {
|
|
||||||
self.messageListRows
|
|
||||||
}
|
|
||||||
|
|
||||||
Color.clear
|
|
||||||
.frame(height: Layout.messageListPaddingBottom)
|
.frame(height: Layout.messageListPaddingBottom)
|
||||||
.id(self.scrollerBottomID)
|
|
||||||
}
|
|
||||||
// Use scroll targets for stable auto-scroll without ScrollViewReader relayout glitches.
|
|
||||||
.scrollTargetLayout()
|
|
||||||
.padding(.top, Layout.messageListPaddingTop)
|
|
||||||
.padding(.horizontal, Layout.messageListPaddingHorizontal)
|
|
||||||
#else
|
#else
|
||||||
LazyVStack(spacing: Layout.messageSpacing) {
|
|
||||||
self.messageListRows
|
|
||||||
|
|
||||||
Color.clear
|
|
||||||
.frame(height: Layout.messageListPaddingBottom + 1)
|
.frame(height: Layout.messageListPaddingBottom + 1)
|
||||||
|
#endif
|
||||||
.id(self.scrollerBottomID)
|
.id(self.scrollerBottomID)
|
||||||
}
|
}
|
||||||
// Use scroll targets for stable auto-scroll without ScrollViewReader relayout glitches.
|
// Use scroll targets for stable auto-scroll without ScrollViewReader relayout glitches.
|
||||||
.scrollTargetLayout()
|
.scrollTargetLayout()
|
||||||
.padding(.top, Layout.messageListPaddingTop)
|
.padding(.top, Layout.messageListPaddingTop)
|
||||||
.padding(.horizontal, Layout.messageListPaddingHorizontal)
|
.padding(.horizontal, Layout.messageListPaddingHorizontal)
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
// Keep the scroll pinned to the bottom for new messages.
|
// Keep the scroll pinned to the bottom for new messages.
|
||||||
.scrollPosition(id: self.$scrollPosition, anchor: .bottom)
|
.scrollPosition(id: self.$scrollPosition, anchor: .bottom)
|
||||||
|
.onChange(of: self.scrollPosition) { _, position in
|
||||||
|
guard let position else { return }
|
||||||
|
self.isPinnedToBottom = position == self.scrollerBottomID
|
||||||
|
}
|
||||||
|
|
||||||
if self.viewModel.isLoading {
|
if self.viewModel.isLoading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
@@ -133,18 +126,26 @@ public struct ClawdbotChatView: View {
|
|||||||
guard !isLoading, !self.hasPerformedInitialScroll else { return }
|
guard !isLoading, !self.hasPerformedInitialScroll else { return }
|
||||||
self.scrollPosition = self.scrollerBottomID
|
self.scrollPosition = self.scrollerBottomID
|
||||||
self.hasPerformedInitialScroll = true
|
self.hasPerformedInitialScroll = true
|
||||||
|
self.isPinnedToBottom = true
|
||||||
}
|
}
|
||||||
.onChange(of: self.viewModel.sessionKey) { _, _ in
|
.onChange(of: self.viewModel.sessionKey) { _, _ in
|
||||||
self.hasPerformedInitialScroll = false
|
self.hasPerformedInitialScroll = false
|
||||||
|
self.isPinnedToBottom = true
|
||||||
}
|
}
|
||||||
.onChange(of: self.viewModel.messages.count) { _, _ in
|
.onChange(of: self.viewModel.messages.count) { _, _ in
|
||||||
guard self.hasPerformedInitialScroll else { return }
|
guard self.hasPerformedInitialScroll, self.isPinnedToBottom else { return }
|
||||||
withAnimation(.snappy(duration: 0.22)) {
|
withAnimation(.snappy(duration: 0.22)) {
|
||||||
self.scrollPosition = self.scrollerBottomID
|
self.scrollPosition = self.scrollerBottomID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: self.viewModel.pendingRunCount) { _, _ in
|
.onChange(of: self.viewModel.pendingRunCount) { _, _ in
|
||||||
guard self.hasPerformedInitialScroll else { return }
|
guard self.hasPerformedInitialScroll, self.isPinnedToBottom else { return }
|
||||||
|
withAnimation(.snappy(duration: 0.22)) {
|
||||||
|
self.scrollPosition = self.scrollerBottomID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: self.viewModel.streamingAssistantText) { _, _ in
|
||||||
|
guard self.hasPerformedInitialScroll, self.isPinnedToBottom else { return }
|
||||||
withAnimation(.snappy(duration: 0.22)) {
|
withAnimation(.snappy(duration: 0.22)) {
|
||||||
self.scrollPosition = self.scrollerBottomID
|
self.scrollPosition = self.scrollerBottomID
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user