Prerequisites
- I have searched for duplicate or closed issues
- I have read the contributing guidelines
Describe the issue
In an editable CodeView with default options, the first three search matches work. Navigating to the fourth match (line 201) jumps to the bottom of the 800-line file instead. The search panel shows 4 of 4, but line 201 is not rendered.
Expected: The selected match scrolls into view, and search and manual scrolling remain usable.
Reproduction
- Mount the component below and click inside the text.
- Open search with Cmd/Ctrl+F and enter github.
- Press Enter three times to select the fourth match.
import type { CodeViewItem } from "@pierre/diffs";
import { Editor, type EditorFactory } from "@pierre/diffs/edit";
import { CodeView, EditProvider } from "@pierre/diffs/react";
const createEditor: EditorFactory<undefined, undefined> = (
editorType,
options,
editStateKey,
) => new Editor(editorType, options, editStateKey);
const matchLines = [125, 126, 128, 201];
const items: CodeViewItem<undefined>[] = [
{
id: "file",
type: "file",
edit: true,
version: 1,
file: {
name: "example.txt",
contents: Array.from({ length: 800 }, (_, index) =>
matchLines.includes(index + 1)
? `github match at ${index + 1}`
: `line ${index + 1}`,
).join("\n"),
},
},
];
export default function SearchReproduction() {
return (
<EditProvider createEditor={createEditor}>
<CodeView
items={items}
style={{ height: 560, overflow: "auto" }}
/>
</EditProvider>
);
}What browser(s) are you seeing the problem on?
Chrome
What version of @pierre/diffs are you using?
1.4.2
Activity
3Thanks for the report! looking into this, i'm also looking at adding full ctrl+f for codeview, so i think i'll tie all that up together there. but it's not a quick thing to add, so please be patient with me 🙏
ofc! thanks for looking into it
Just stumbled upon this (1.3.5, 1.5.2) and had Opus 5.5 high find a patch for those impacted who want a quick-and-dirty fix.
Per Claude:
When the target line isn't rendered, Editor#scrollToLine sets the virtual caret's top to getLinePosition().top and appends it to the file container's shadow root. Under CodeView that host element has already been moved down to the rendered window (by bufferBefore), so the caret ends up bufferBefore px too low. scrollIntoView overshoots, the line never renders, and #scrollingToLine stays set, so every later render (user scrolls included) snaps back to the bottom. That's why scrolling stays stuck even after closing the search panel.
Converting the model top through any rendered line fixes it for us:
top = modelTop - getLinePosition(refLine).top + (refLineEl.top - host.top).
We apply it as a local patch to the published dist/ (bun patch @pierre/diffs; the npm equivalent is npx patch-package @pierre/diffs, pnpm has pnpm patch). Against 1.5.2 dist/editor/editor.js:
@@ -2096,6 +2096,21 @@ var Editor = class {
+ #modelTopInHost(modelTop) {
+ const host = this.#fileContainer;
+ const contentEl = this.#contentElement;
+ if (host == null || contentEl == null) return modelTop;
+ for (const child of contentEl.children) {
+ const lineNumber = getLineNumberAttr(child);
+ if (lineNumber === void 0) continue;
+ const refPosition = this.#virtualizedInstance?.getLinePosition?.(lineNumber);
+ if (refPosition === void 0) continue;
+ const refTop = child.getBoundingClientRect().top - host.getBoundingClientRect().top;
+ return modelTop - refPosition.top + refTop;
+ }
+ return modelTop;
+ }
#scrollToLine(line, char = 0, noFocus = false) {
@@ -2130,7 +2145,7 @@
const modelLinePosition = this.#virtualizedInstance?.getLinePosition?.(line + 1);
if (modelLinePosition !== void 0) {
- virtualCaret.style.top = modelLinePosition.top + "px";
+ virtualCaret.style.top = this.#modelTopInHost(modelLinePosition.top) + "px";
Thanks for the report! looking into this, i'm also looking at adding full ctrl+f for codeview, so i think i'll tie all that up together there. but it's not a quick thing to add, so please be patient with me 🙏