fix: notes content search next scroll (#10908)

* fix: topic branch incomplete copy - split ID mapping into two passes

Fix the bug where topic branching would not copy all message relationships completely.The issue was that askId mapping lookup happened in the same loop as ID generation, causing later messages' askIds to fail mapping when they referenced messages that hadn't been processed yet.

Solution: Split into two passes:
 1. First pass: Generate new IDs for all messages and build complete mapping
 2. Second pass: Clone messages and blocks using the complete ID mapping

This ensures all message relationships (especially assistant message askId references)are properly maintained in the new topic.

* fix(notes): 保持 Ctrl+F ‘下一个’在编辑器容器内滚动,避免索引提前回到第一条

- 使用传入的滚动容器计算相对偏移并 target.scrollTo 居中
- 容器不可滚动时回退到 scrollIntoView,兼容其他页面
- 将 target 纳入依赖,确保引用最新容器

受影响文件:
- src/renderer/src/components/ContentSearch.tsx:165

* fix(search): improve notes content search next-scroll behavior

* Update dom.ts

---------

Co-authored-by: Pleasurecruise <3196812536@qq.com>
This commit is contained in:
Zhaokun 2025-11-14 11:51:18 +08:00 committed by GitHub
parent 35aa9d7355
commit 75fcf8fbb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 7 deletions

View File

@ -1,5 +1,6 @@
import { ActionIconButton } from '@renderer/components/Buttons'
import NarrowLayout from '@renderer/pages/home/Messages/NarrowLayout'
import { scrollElementIntoView } from '@renderer/utils'
import { Tooltip } from 'antd'
import { debounce } from 'lodash'
import { CaseSensitive, ChevronDown, ChevronUp, User, WholeWord, X } from 'lucide-react'
@ -181,17 +182,14 @@ export const ContentSearch = React.forwardRef<ContentSearchRef, Props>(
// 3. 将当前项滚动到视图中
// 获取第一个文本节点的父元素来进行滚动
const parentElement = currentMatchRange.startContainer.parentElement
if (shouldScroll) {
parentElement?.scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'nearest'
})
if (shouldScroll && parentElement) {
// 优先在指定的滚动容器内滚动,避免滚动整个页面导致索引错乱/看起来"跳到第一条"
scrollElementIntoView(parentElement, target)
}
}
}
},
[allRanges, currentIndex]
[allRanges, currentIndex, target]
)
const search = useCallback(

View File

@ -0,0 +1,55 @@
/**
* Simple wrapper for scrollIntoView with common default options.
* Provides a unified interface with sensible defaults.
*
* @param element - The target element to scroll into view
* @param options - Scroll options. If not provided, uses { behavior: 'smooth', block: 'center', inline: 'nearest' }
*/
export function scrollIntoView(element: HTMLElement, options?: ScrollIntoViewOptions): void {
const defaultOptions: ScrollIntoViewOptions = {
behavior: 'smooth',
block: 'center',
inline: 'nearest'
}
element.scrollIntoView(options ?? defaultOptions)
}
/**
* Intelligently scrolls an element into view at the center position.
* Prioritizes scrolling within the specified container to avoid scrolling the entire page.
*
* @param element - The target element to scroll into view
* @param scrollContainer - Optional scroll container. If provided and scrollable, scrolling happens within it; otherwise uses browser default scrolling
* @param behavior - Scroll behavior, defaults to 'smooth'
*/
export function scrollElementIntoView(
element: HTMLElement,
scrollContainer?: HTMLElement | null,
behavior: ScrollBehavior = 'smooth'
): void {
if (!scrollContainer) {
// No container specified, use browser default scrolling
scrollIntoView(element, { behavior, block: 'center', inline: 'nearest' })
return
}
// Check if container is scrollable
const canScroll =
scrollContainer.scrollHeight > scrollContainer.clientHeight ||
scrollContainer.scrollWidth > scrollContainer.clientWidth
if (canScroll) {
// Container is scrollable, scroll within the container
const containerRect = scrollContainer.getBoundingClientRect()
const elRect = element.getBoundingClientRect()
// Calculate element's scrollable offset position relative to the container
const elementTopWithinContainer = elRect.top - containerRect.top + scrollContainer.scrollTop
const desiredTop = elementTopWithinContainer - Math.max(0, scrollContainer.clientHeight - elRect.height) / 2
scrollContainer.scrollTo({ top: Math.max(0, desiredTop), behavior })
} else {
// Container is not scrollable, fallback to browser default scrolling
scrollIntoView(element, { behavior, block: 'center', inline: 'nearest' })
}
}

View File

@ -214,6 +214,7 @@ export function uniqueObjectArray<T>(array: T[]): T[] {
export * from './api'
export * from './collection'
export * from './dataLimit'
export * from './dom'
export * from './file'
export * from './image'
export * from './json'