mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-28 06:22:33 +00:00
refac
This commit is contained in:
parent
079fb19e75
commit
32170b96fc
2 changed files with 47 additions and 99 deletions
|
|
@ -27,7 +27,7 @@
|
|||
lineAlignment,
|
||||
alignChangedLines,
|
||||
clearLineAlignment,
|
||||
pairedLinePosition
|
||||
alignmentGeometryChanged
|
||||
} from './diffEditorAlignment';
|
||||
import {
|
||||
diffPresentation,
|
||||
|
|
@ -371,7 +371,7 @@
|
|||
};
|
||||
updatePresentation = schedulePresentation;
|
||||
let alignmentDocuments: readonly [Text, Text] | null = null;
|
||||
let alignmentGeometry = '';
|
||||
let alignmentGeometry: readonly number[] | null = null;
|
||||
const themes = [new Compartment(), new Compartment()];
|
||||
const language = [new Compartment(), new Compartment()];
|
||||
const paths = ['a', 'b'].map((side) => paneFile(side)?.path ?? session?.path);
|
||||
|
|
@ -423,47 +423,18 @@
|
|||
view.b.contentDOM.clientWidth,
|
||||
view.a.defaultLineHeight,
|
||||
view.b.defaultLineHeight
|
||||
].join(':');
|
||||
];
|
||||
const resized =
|
||||
alignmentGeometry && alignmentGeometryChanged(alignmentGeometry, geometry);
|
||||
const reset =
|
||||
alignmentDocuments &&
|
||||
(alignmentDocuments[0] !== documents[0] ||
|
||||
alignmentDocuments[1] !== documents[1] ||
|
||||
geometry !== alignmentGeometry);
|
||||
const resized = alignmentDocuments && geometry !== alignmentGeometry;
|
||||
resized);
|
||||
alignmentDocuments = documents;
|
||||
alignmentGeometry = geometry;
|
||||
const previousSpacersA = view.a.state.field(lineAlignment);
|
||||
const previousSpacersB = view.b.state.field(lineAlignment);
|
||||
if (!alignmentGeometry || resized) alignmentGeometry = geometry;
|
||||
if (!reset || !clearLineAlignment(view.a, view.b))
|
||||
alignChangedLines(view.a, view.b, view.chunks);
|
||||
if (resized) {
|
||||
// Reflow can leave one viewport entirely inside an old EOF spacer.
|
||||
// Measure both panes around the row the reader was looking at.
|
||||
const top = Math.max(0, node.getBoundingClientRect().top - view.a.documentTop);
|
||||
const position = Math.min(
|
||||
view.a.lineBlockAtHeight(top).from,
|
||||
view.a.state.doc.length
|
||||
);
|
||||
const other = pairedLinePosition(
|
||||
view.a.state.doc,
|
||||
view.b.state.doc,
|
||||
view.chunks,
|
||||
position
|
||||
);
|
||||
view.a.dispatch({
|
||||
effects: EditorView.scrollIntoView(position, { y: 'start', yMargin: 0 })
|
||||
});
|
||||
view.b.dispatch({
|
||||
effects: EditorView.scrollIntoView(other, { y: 'start', yMargin: 0 })
|
||||
});
|
||||
}
|
||||
// Remeasuring unchanged spacers feeds CodeMirror's own layout updates back into itself.
|
||||
if (
|
||||
resized ||
|
||||
previousSpacersA !== view.a.state.field(lineAlignment) ||
|
||||
previousSpacersB !== view.b.state.field(lineAlignment)
|
||||
)
|
||||
view.reconfigure({});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { StateEffect, StateField, type Text } from '@codemirror/state';
|
||||
import { StateEffect, StateField } from '@codemirror/state';
|
||||
import {
|
||||
BlockType,
|
||||
Decoration,
|
||||
|
|
@ -9,21 +9,9 @@ import {
|
|||
import type { Chunk } from '@codemirror/merge';
|
||||
import { changedLinePairs } from './diffEditor';
|
||||
|
||||
export function pairedLinePosition(a: Text, b: Text, chunks: readonly Chunk[], position: number) {
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
if (position < chunk.fromA) break;
|
||||
if (position < chunk.toA) {
|
||||
const pairs = changedLinePairs(a, b, chunk);
|
||||
const line = a.lineAt(position).from;
|
||||
return (
|
||||
(pairs.find(([from]) => from >= line) ?? pairs.at(-1))?.[1] ??
|
||||
Math.min(chunk.fromB, b.length)
|
||||
);
|
||||
}
|
||||
offset = chunk.toB - chunk.toA;
|
||||
}
|
||||
return Math.max(0, Math.min(position + offset, b.length));
|
||||
export function alignmentGeometryChanged(previous: readonly number[], next: readonly number[]) {
|
||||
// DOM line-height measurements can vary by a fraction of a pixel while scrolling.
|
||||
return next.some((value, index) => Math.abs(value - previous[index]) > (index < 2 ? 0 : 0.5));
|
||||
}
|
||||
|
||||
class RowSpacer extends WidgetType {
|
||||
|
|
@ -64,8 +52,8 @@ export function clearLineAlignment(a: EditorView, b: EditorView) {
|
|||
return changed;
|
||||
}
|
||||
|
||||
// Only remeasure paired rows present in both virtualized viewports. Recomputing
|
||||
// offscreen spacers from estimated heights would move already aligned content.
|
||||
// MergeView owns alignment between chunks. Only align rows inside each changed
|
||||
// chunk, using local heights so its spacers cannot feed back into ours.
|
||||
export function alignChangedLines(a: EditorView, b: EditorView, chunks: readonly Chunk[]) {
|
||||
const previous = (view: EditorView) => {
|
||||
const result = new Map<number, number>();
|
||||
|
|
@ -77,8 +65,6 @@ export function alignChangedLines(a: EditorView, b: EditorView, chunks: readonly
|
|||
oldB = previous(b);
|
||||
const nextA = new Map(oldA),
|
||||
nextB = new Map(oldB);
|
||||
let offsetA = 0,
|
||||
offsetB = 0;
|
||||
const textTop = (view: EditorView, pos: number) => {
|
||||
const block = view.lineBlockAt(pos);
|
||||
const text = Array.isArray(block.type)
|
||||
|
|
@ -88,52 +74,43 @@ export function alignChangedLines(a: EditorView, b: EditorView, chunks: readonly
|
|||
: null;
|
||||
return text?.from === pos ? text.top : null;
|
||||
};
|
||||
const align = (posA: number, posB: number) => {
|
||||
for (const chunk of chunks) {
|
||||
if (
|
||||
posA < a.viewport.from ||
|
||||
posB < b.viewport.from ||
|
||||
posA > a.viewport.to ||
|
||||
posB > b.viewport.to
|
||||
chunk.toA < a.viewport.from ||
|
||||
chunk.toB < b.viewport.from ||
|
||||
chunk.fromA > a.viewport.to ||
|
||||
chunk.fromB > b.viewport.to
|
||||
)
|
||||
return;
|
||||
const topA = textTop(a, posA),
|
||||
topB = textTop(b, posB);
|
||||
if (topA === null || topB === null) return;
|
||||
const previousA = oldA.get(posA) ?? 0,
|
||||
previousB = oldB.get(posB) ?? 0;
|
||||
const difference = topA - previousA + offsetA - topB + previousB - offsetB;
|
||||
const sizeA = difference < -1 ? -difference : 0,
|
||||
sizeB = difference > 1 ? difference : 0;
|
||||
nextA.delete(posA);
|
||||
nextB.delete(posB);
|
||||
if (sizeA) nextA.set(posA, sizeA);
|
||||
if (sizeB) nextB.set(posB, sizeB);
|
||||
offsetA += sizeA - previousA;
|
||||
offsetB += sizeB - previousB;
|
||||
};
|
||||
let fromA = 0,
|
||||
fromB = 0;
|
||||
for (let index = 0; index <= chunks.length; index++) {
|
||||
const chunk = chunks[index];
|
||||
const endA = chunk ? chunk.fromA : a.state.doc.length + 1;
|
||||
let posA = Math.max(
|
||||
fromA,
|
||||
a.state.doc.lineAt(
|
||||
Math.min(
|
||||
a.state.doc.length,
|
||||
Math.max(fromA, a.viewport.from, fromA + b.viewport.from - fromB)
|
||||
)
|
||||
).from
|
||||
);
|
||||
for (; posA < endA && posA <= a.viewport.to; posA = a.state.doc.lineAt(posA).to + 1) {
|
||||
const posB = fromB + posA - fromA;
|
||||
if (posB > b.viewport.to) break;
|
||||
align(posA, posB);
|
||||
continue;
|
||||
let previousA = chunk.fromA,
|
||||
previousB = chunk.fromB,
|
||||
first = true;
|
||||
for (const [posA, posB] of changedLinePairs(a.state.doc, b.state.doc, chunk)) {
|
||||
// Both ends of the interval must be rendered; offscreen estimates are not row heights.
|
||||
if (
|
||||
previousA >= a.viewport.from &&
|
||||
previousB >= b.viewport.from &&
|
||||
posA <= a.viewport.to &&
|
||||
posB <= b.viewport.to
|
||||
) {
|
||||
const topA = textTop(a, posA),
|
||||
topB = textTop(b, posB);
|
||||
const startA = first ? a.lineBlockAt(previousA).top : textTop(a, previousA);
|
||||
const startB = first ? b.lineBlockAt(previousB).top : textTop(b, previousB);
|
||||
if (topA !== null && topB !== null && startA !== null && startB !== null) {
|
||||
const heightA = topA - startA - (oldA.get(posA) ?? 0);
|
||||
const heightB = topB - startB - (oldB.get(posB) ?? 0);
|
||||
const difference = heightA - heightB;
|
||||
nextA.delete(posA);
|
||||
nextB.delete(posB);
|
||||
if (difference < -1) nextA.set(posA, -difference);
|
||||
if (difference > 1) nextB.set(posB, difference);
|
||||
}
|
||||
}
|
||||
previousA = posA;
|
||||
previousB = posB;
|
||||
first = false;
|
||||
}
|
||||
if (!chunk) break;
|
||||
for (const [posA, posB] of changedLinePairs(a.state.doc, b.state.doc, chunk)) align(posA, posB);
|
||||
fromA = chunk.toA;
|
||||
fromB = chunk.toB;
|
||||
}
|
||||
const apply = (view: EditorView, old: Map<number, number>, next: Map<number, number>) => {
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue