mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: implement auto-scroll to first <DiffEditor /> diff (#21967)
Closes #21962 This pull-request makes it so that we auto-scroll to our first diff within the files when the page loads. It attempts to center it within the inner viewports scroll. | Old | New | | --- | --- | | <img width="3516" height="2390" alt="CleanShot 2026-02-06 at 17 12 23@2x" src="https://github.com/user-attachments/assets/2215178d-b887-4d3b-a5a2-882ad4b1f03c" /> | <img width="3516" height="2390" alt="CleanShot 2026-02-06 at 17 11 53@2x" src="https://github.com/user-attachments/assets/4b28c589-ebee-4e8c-ac44-22717f80023c" />
This commit is contained in:
@@ -1,16 +1,30 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Editor, { DiffEditor, loader } from "@monaco-editor/react";
|
||||
import type * as Monaco from "monaco-editor";
|
||||
import * as monaco from "monaco-editor";
|
||||
import type { ComponentProps, FC } from "react";
|
||||
import { type ComponentProps, type FC, useCallback } from "react";
|
||||
import { useCoderTheme } from "./coderTheme";
|
||||
|
||||
loader.config({ monaco });
|
||||
|
||||
// Shared editor props with onMount typed to accept either editor variant,
|
||||
// so callers don't need to know which underlying component will render.
|
||||
type CommonEditorProps = Omit<
|
||||
ComponentProps<typeof Editor> & ComponentProps<typeof DiffEditor>,
|
||||
"onMount"
|
||||
> & {
|
||||
onMount?: (
|
||||
editor:
|
||||
| Monaco.editor.IStandaloneCodeEditor
|
||||
| Monaco.editor.IStandaloneDiffEditor,
|
||||
monaco: typeof Monaco,
|
||||
) => void;
|
||||
};
|
||||
|
||||
interface SyntaxHighlighterProps {
|
||||
value: string;
|
||||
language?: string;
|
||||
editorProps?: ComponentProps<typeof Editor> &
|
||||
ComponentProps<typeof DiffEditor>;
|
||||
editorProps?: CommonEditorProps;
|
||||
compareWith?: string;
|
||||
}
|
||||
|
||||
@@ -23,6 +37,41 @@ export const SyntaxHighlighter: FC<SyntaxHighlighterProps> = ({
|
||||
const hasDiff = compareWith && value !== compareWith;
|
||||
const theme = useTheme();
|
||||
const coderTheme = useCoderTheme();
|
||||
|
||||
// Auto-scroll to first diff when the diff editor mounts and diffs are computed.
|
||||
const handleDiffEditorMount = useCallback(
|
||||
(
|
||||
editor: Monaco.editor.IStandaloneDiffEditor,
|
||||
monacoInstance: typeof Monaco,
|
||||
) => {
|
||||
// Call any existing onMount handler from editorProps.
|
||||
editorProps?.onMount?.(editor, monacoInstance);
|
||||
|
||||
// Diffs may already be computed by the time onMount fires,
|
||||
// so check immediately first. If not ready yet, fall back
|
||||
// to waiting for the onDidUpdateDiff event.
|
||||
const scrollToFirstDiff = () => {
|
||||
editor.goToDiff("next");
|
||||
};
|
||||
|
||||
const changes = editor.getLineChanges();
|
||||
if (changes && changes.length > 0) {
|
||||
scrollToFirstDiff();
|
||||
return;
|
||||
}
|
||||
|
||||
const disposable = editor.onDidUpdateDiff(() => {
|
||||
const updatedChanges = editor.getLineChanges();
|
||||
if (!updatedChanges || updatedChanges.length === 0) {
|
||||
return;
|
||||
}
|
||||
disposable.dispose();
|
||||
scrollToFirstDiff();
|
||||
});
|
||||
},
|
||||
[editorProps],
|
||||
);
|
||||
|
||||
const commonProps = {
|
||||
language,
|
||||
theme: coderTheme.name,
|
||||
@@ -50,7 +99,12 @@ export const SyntaxHighlighter: FC<SyntaxHighlighterProps> = ({
|
||||
}}
|
||||
>
|
||||
{hasDiff ? (
|
||||
<DiffEditor original={compareWith} modified={value} {...commonProps} />
|
||||
<DiffEditor
|
||||
original={compareWith}
|
||||
modified={value}
|
||||
{...commonProps}
|
||||
onMount={handleDiffEditorMount}
|
||||
/>
|
||||
) : (
|
||||
<Editor value={value} {...commonProps} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user