Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
"katex": "^0.16.27",
"lint-staged": "^16.4.0",
"lucide-vue-next": "^0.544.0",
"markstream-vue": "0.0.8-beta.1",
"markstream-vue": "0.0.10-beta.6",
"mermaid": "^11.13.0",
"minimatch": "^10.2.4",
"monaco-editor": "^0.52.2",
Expand All @@ -165,7 +165,7 @@
"pinia": "^3.0.4",
"reka-ui": "^2.9.2",
"simple-git-hooks": "^2.13.1",
"stream-monaco": "^0.0.20",
"stream-monaco": "^0.0.21",
"tailwind-merge": "^3.5.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.2.2",
Expand Down
18 changes: 12 additions & 6 deletions src/renderer/src/composables/message/useMessageScroll.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ref, reactive, readonly, onBeforeUnmount, nextTick, type Ref } from 'vue'
import { useDebounceFn } from '@vueuse/core'
import type { ScrollInfo } from './types'
import type { DynamicScroller } from 'vue-virtual-scroller'

// === Constants ===
const MESSAGE_HIGHLIGHT_CLASS = 'message-highlight'
Expand All @@ -10,8 +9,13 @@ const SCROLL_RETRY_DELAY = 80
const HIGHLIGHT_DURATION = 2000
const PLACEHOLDER_POSITION_THRESHOLD = 5000

type DynamicScrollerHandle = {
scrollToBottom?: () => void
scrollToItem?: (index: number) => void
}

export interface UseMessageScrollOptions {
dynamicScrollerRef?: Ref<InstanceType<typeof DynamicScroller> | null>
dynamicScrollerRef?: Ref<DynamicScrollerHandle | null>
shouldAutoFollow?: Ref<boolean>
autoScrollEnabled?: Ref<boolean>
scrollAnchor?: Ref<HTMLDivElement | undefined>
Expand Down Expand Up @@ -94,15 +98,16 @@ export function useMessageScroll(options?: UseMessageScrollOptions) {

const dynamicScrollerRef = options?.dynamicScrollerRef
const scroller = dynamicScrollerRef?.value
const scrollToBottomFn = scroller?.scrollToBottom

if (scroller?.scrollToBottom) {
if (scrollToBottomFn) {
// Virtual scroll with retry mechanism
let retryCount = 0
let lastScrollHeight = 0

const attemptScrollToBottom = () => {
if (currentBottomToken !== bottomScrollCancelToken) return
scroller.scrollToBottom()
scrollToBottomFn()

nextTick(() => {
bottomScrollRetryTimer = window.setTimeout(() => {
Expand Down Expand Up @@ -177,8 +182,9 @@ export function useMessageScroll(options?: UseMessageScrollOptions) {
const scrollToMessage = (messageId: string, itemsGetter?: () => Array<{ id: string }>) => {
const dynamicScrollerRef = options?.dynamicScrollerRef
const scroller = dynamicScrollerRef?.value
const scrollToItemFn = scroller?.scrollToItem

if (!scroller?.scrollToItem || !itemsGetter) {
if (!scrollToItemFn || !itemsGetter) {
scrollToMessageBase(messageId)
return
}
Expand Down Expand Up @@ -227,7 +233,7 @@ export function useMessageScroll(options?: UseMessageScrollOptions) {
const attemptScroll = () => {
if (currentToken !== scrollRetryToken) return

scroller.scrollToItem(index)
scrollToItemFn(index)
nextTick(() => {
setTimeout(() => {
if (tryApplyCenterAndHighlight()) return
Expand Down
Loading