@bochenw/react-diff-viewer-continued 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +521 -0
  3. package/lib/cjs/src/comment-row.d.ts +33 -0
  4. package/lib/cjs/src/comment-row.js +58 -0
  5. package/lib/cjs/src/compute-hidden-blocks.d.ts +13 -0
  6. package/lib/cjs/src/compute-hidden-blocks.js +36 -0
  7. package/lib/cjs/src/compute-lines.d.ts +68 -0
  8. package/lib/cjs/src/compute-lines.js +559 -0
  9. package/lib/cjs/src/computeWorker.d.ts +1 -0
  10. package/lib/cjs/src/computeWorker.js +10 -0
  11. package/lib/cjs/src/diff-row.d.ts +40 -0
  12. package/lib/cjs/src/diff-row.js +136 -0
  13. package/lib/cjs/src/expand.d.ts +1 -0
  14. package/lib/cjs/src/expand.js +4 -0
  15. package/lib/cjs/src/fold.d.ts +1 -0
  16. package/lib/cjs/src/fold.js +4 -0
  17. package/lib/cjs/src/index.d.ts +236 -0
  18. package/lib/cjs/src/index.js +783 -0
  19. package/lib/cjs/src/line-number-prefix.d.ts +4 -0
  20. package/lib/cjs/src/line-number-prefix.js +5 -0
  21. package/lib/cjs/src/render-word-diff.d.ts +22 -0
  22. package/lib/cjs/src/render-word-diff.js +212 -0
  23. package/lib/cjs/src/skipped-line-indicator.d.ts +29 -0
  24. package/lib/cjs/src/skipped-line-indicator.js +29 -0
  25. package/lib/cjs/src/styles.d.ts +102 -0
  26. package/lib/cjs/src/styles.js +430 -0
  27. package/lib/cjs/src/workerBundle.d.ts +5 -0
  28. package/lib/cjs/src/workerBundle.js +7 -0
  29. package/lib/esm/src/comment-row.js +58 -0
  30. package/lib/esm/src/compute-hidden-blocks.js +36 -0
  31. package/lib/esm/src/compute-lines.js +559 -0
  32. package/lib/esm/src/computeWorker.js +10 -0
  33. package/lib/esm/src/diff-row.js +136 -0
  34. package/lib/esm/src/expand.js +4 -0
  35. package/lib/esm/src/fold.js +4 -0
  36. package/lib/esm/src/index.js +780 -0
  37. package/lib/esm/src/line-number-prefix.js +5 -0
  38. package/lib/esm/src/render-word-diff.js +211 -0
  39. package/lib/esm/src/skipped-line-indicator.js +29 -0
  40. package/lib/esm/src/styles.js +431 -0
  41. package/lib/esm/src/workerBundle.js +7 -0
  42. package/package.json +90 -0
@@ -0,0 +1,783 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import cn from "classnames";
12
+ import * as React from "react";
13
+ import memoize from "memoize-one";
14
+ import { computeHiddenBlocks } from "./compute-hidden-blocks.js";
15
+ import { DiffMethod, DiffType, computeLineInformationWorker, computeDiff, } from "./compute-lines.js";
16
+ import { Expand } from "./expand.js";
17
+ import computeStyles from "./styles.js";
18
+ import { Fold } from "./fold.js";
19
+ import { LineNumberPrefix } from "./line-number-prefix.js";
20
+ import { DiffRow } from "./diff-row.js";
21
+ import { SkippedLineIndicator } from "./skipped-line-indicator.js";
22
+ import { CommentRow } from "./comment-row.js";
23
+ export { LineNumberPrefix } from "./line-number-prefix.js";
24
+ class DiffViewer extends React.Component {
25
+ /**
26
+ * Shallow comparison for string arrays — avoids unnecessary work when
27
+ * the consumer creates a new array reference with identical contents.
28
+ */
29
+ static shallowArrayEqual(a, b) {
30
+ if (a === b)
31
+ return true;
32
+ if (!a || !b || a.length !== b.length)
33
+ return false;
34
+ for (let i = 0; i < a.length; i++) {
35
+ if (a[i] !== b[i])
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+ constructor(props) {
41
+ super(props);
42
+ // Cache for on-demand word diff computation
43
+ this.wordDiffCache = new Map();
44
+ // Refs for measuring content column width and character width
45
+ this.contentColumnRef = React.createRef();
46
+ this.charMeasureRef = React.createRef();
47
+ this.stickyHeaderRef = React.createRef();
48
+ this.resizeObserver = null;
49
+ // Comment row height measurement for virtualization
50
+ this.commentRowObserver = null;
51
+ this.commentRowHeights = new Map();
52
+ this.commentRowElements = new Map();
53
+ this.commentRowRefCache = new Map();
54
+ this.pendingOffsetRecalc = false;
55
+ /**
56
+ * Memoized conversion of commentLineIds array to Set for O(1) lookups.
57
+ */
58
+ this.getCommentLineIdsSet = memoize((ids) => new Set(ids || []));
59
+ /**
60
+ * Memoized conversion of highlightLines array to Set for O(1) lookups.
61
+ */
62
+ this.getHighlightLinesSet = memoize((lines) => new Set(lines || []));
63
+ /**
64
+ * Creates a ref callback for a CommentRow's <tr> element.
65
+ * When mounted, observes it for height changes via ResizeObserver.
66
+ * Callbacks are cached per lineId to avoid creating new closures on every render.
67
+ */
68
+ this.getCommentRowRef = (lineId) => {
69
+ let cached = this.commentRowRefCache.get(lineId);
70
+ if (!cached) {
71
+ cached = (el) => {
72
+ var _a, _b;
73
+ if (el) {
74
+ this.commentRowElements.set(lineId, el);
75
+ (_a = this.commentRowObserver) === null || _a === void 0 ? void 0 : _a.observe(el);
76
+ }
77
+ else {
78
+ const prev = this.commentRowElements.get(lineId);
79
+ if (prev) {
80
+ (_b = this.commentRowObserver) === null || _b === void 0 ? void 0 : _b.unobserve(prev);
81
+ }
82
+ this.commentRowElements.delete(lineId);
83
+ }
84
+ };
85
+ this.commentRowRefCache.set(lineId, cached);
86
+ }
87
+ return cached;
88
+ };
89
+ /**
90
+ * Debounced offset recalculation — batches multiple ResizeObserver callbacks
91
+ * into a single requestAnimationFrame.
92
+ */
93
+ this.scheduleOffsetRecalc = () => {
94
+ if (!this.pendingOffsetRecalc) {
95
+ this.pendingOffsetRecalc = true;
96
+ requestAnimationFrame(() => {
97
+ this.pendingOffsetRecalc = false;
98
+ this.recalculateOffsets();
99
+ });
100
+ }
101
+ };
102
+ /**
103
+ * Initializes the ResizeObserver for measuring comment row heights.
104
+ */
105
+ this.initCommentRowObserver = () => {
106
+ if (typeof ResizeObserver === "undefined" || this.commentRowObserver)
107
+ return;
108
+ this.commentRowObserver = new ResizeObserver((entries) => {
109
+ var _a, _b, _c;
110
+ let changed = false;
111
+ for (const entry of entries) {
112
+ const el = entry.target;
113
+ const lineId = el.getAttribute("data-comment-line");
114
+ if (!lineId)
115
+ continue;
116
+ const height = (_c = (_b = (_a = entry.borderBoxSize) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.blockSize) !== null && _c !== void 0 ? _c : el.offsetHeight;
117
+ const prev = this.commentRowHeights.get(lineId);
118
+ if (prev !== height) {
119
+ this.commentRowHeights.set(lineId, height);
120
+ changed = true;
121
+ }
122
+ }
123
+ if (changed && this.props.infiniteLoading) {
124
+ this.scheduleOffsetRecalc();
125
+ }
126
+ });
127
+ };
128
+ /**
129
+ * Computes word diff on-demand for a line, with caching.
130
+ * This is used when word diff was deferred during initial computation.
131
+ */
132
+ this.getWordDiffValues = (left, right, lineIndex) => {
133
+ // Handle empty left/right
134
+ if (!left || !right) {
135
+ return { leftValue: left === null || left === void 0 ? void 0 : left.value, rightValue: right === null || right === void 0 ? void 0 : right.value };
136
+ }
137
+ // If no raw values, word diff was already computed or disabled
138
+ // Use explicit undefined check since empty string is a valid raw value
139
+ if (left.rawValue === undefined || right.rawValue === undefined) {
140
+ return { leftValue: left.value, rightValue: right.value };
141
+ }
142
+ // Check cache
143
+ const cacheKey = `${lineIndex}-${left.rawValue}-${right.rawValue}`;
144
+ let cached = this.wordDiffCache.get(cacheKey);
145
+ if (!cached) {
146
+ // Compute word diff on-demand
147
+ // Use CHARS method for on-demand computation since rawValue is always a string
148
+ // (JSON/YAML methods only work with objects, not the string lines we have here)
149
+ const compareMethod = (this.props.compareMethod === DiffMethod.JSON || this.props.compareMethod === DiffMethod.YAML)
150
+ ? DiffMethod.CHARS
151
+ : this.props.compareMethod;
152
+ const computed = computeDiff(left.rawValue, right.rawValue, compareMethod);
153
+ cached = { left: computed.left, right: computed.right };
154
+ this.wordDiffCache.set(cacheKey, cached);
155
+ }
156
+ return { leftValue: cached.left, rightValue: cached.right };
157
+ };
158
+ /**
159
+ * Resets code block expand to the initial stage. Will be exposed to the parent component via
160
+ * refs.
161
+ */
162
+ this.resetCodeBlocks = () => {
163
+ if (this.state.expandedBlocks.length > 0) {
164
+ this.setState({
165
+ expandedBlocks: [],
166
+ });
167
+ return true;
168
+ }
169
+ return false;
170
+ };
171
+ /**
172
+ * Pushes the target expanded code block to the state. During the re-render,
173
+ * this value is used to expand/fold unmodified code.
174
+ */
175
+ this.onBlockExpand = (id) => {
176
+ const prevState = this.state.expandedBlocks.slice();
177
+ prevState.push(id);
178
+ this.setState({ expandedBlocks: prevState }, () => this.recalculateOffsets());
179
+ };
180
+ /**
181
+ * Recalculates cumulative offsets based on current measurements.
182
+ * Called on resize and when blocks are expanded/collapsed.
183
+ */
184
+ this.recalculateOffsets = () => {
185
+ var _a;
186
+ if (!this.props.infiniteLoading)
187
+ return;
188
+ const columnWidth = this.measureContentColumnWidth();
189
+ const charWidth = this.measureCharWidth();
190
+ if (!columnWidth)
191
+ return;
192
+ const cacheKey = this.getMemoisedKey();
193
+ const { lineInformation, lineBlocks, blocks } = (_a = this.state.computedDiffResult[cacheKey]) !== null && _a !== void 0 ? _a : {};
194
+ if (!lineInformation)
195
+ return;
196
+ const offsets = this.buildCumulativeOffsets(lineInformation, lineBlocks, blocks, this.state.expandedBlocks, this.props.showDiffOnly, charWidth, columnWidth, this.props.splitView, this.getCommentLineIdsSet(this.props.commentLineIds));
197
+ this.setState({ cumulativeOffsets: offsets, contentColumnWidth: columnWidth, charWidth }, () => {
198
+ // Force a scroll position update to recalculate visible rows with new offsets
199
+ this.onScroll();
200
+ });
201
+ };
202
+ /**
203
+ * Computes final styles for the diff viewer. It combines the default styles with the user
204
+ * supplied overrides. The computed styles are cached with performance in mind.
205
+ *
206
+ * @param styles User supplied style overrides.
207
+ */
208
+ this.computeStyles = memoize(computeStyles);
209
+ /**
210
+ *
211
+ * Generates a unique cache key based on the current props used in diff computation.
212
+ *
213
+ * This key is used to memoize results and avoid recomputation for the same inputs.
214
+ * @returns A stringified JSON key representing the current diff settings and input values.
215
+ *
216
+ */
217
+ this.getMemoisedKey = () => {
218
+ const { oldValue, newValue, disableWordDiff, compareMethod, linesOffset, alwaysShowLines, extraLinesSurroundingDiff, } = this.props;
219
+ return JSON.stringify({
220
+ oldValue,
221
+ newValue,
222
+ disableWordDiff,
223
+ compareMethod,
224
+ linesOffset,
225
+ alwaysShowLines,
226
+ extraLinesSurroundingDiff,
227
+ });
228
+ };
229
+ /**
230
+ * Computes and memoizes the diff result between `oldValue` and `newValue`.
231
+ *
232
+ * If a memoized result exists for the current input configuration, it uses that.
233
+ * Otherwise, it runs the diff logic in a Web Worker to avoid blocking the UI.
234
+ * It also computes hidden line blocks for collapsing unchanged sections,
235
+ * and stores the result in the local component state.
236
+ */
237
+ this.memoisedCompute = () => __awaiter(this, void 0, void 0, function* () {
238
+ var _a;
239
+ const { oldValue, newValue, disableWordDiff, compareMethod, linesOffset } = this.props;
240
+ const cacheKey = this.getMemoisedKey();
241
+ if (!!this.state.computedDiffResult[cacheKey]) {
242
+ this.setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false })));
243
+ return;
244
+ }
245
+ // Defer word diff computation when using infinite loading with reasonable container height
246
+ // This significantly improves initial render time for large diffs
247
+ const containerHeight = (_a = this.props.infiniteLoading) === null || _a === void 0 ? void 0 : _a.containerHeight;
248
+ const containerHeightPx = containerHeight
249
+ ? typeof containerHeight === 'number'
250
+ ? containerHeight
251
+ : parseInt(containerHeight, 10) || 0
252
+ : 0;
253
+ const shouldDeferWordDiff = !disableWordDiff &&
254
+ !!this.props.infiniteLoading &&
255
+ containerHeightPx > 0 &&
256
+ containerHeightPx < 2000;
257
+ const { lineInformation, diffLines } = yield computeLineInformationWorker(oldValue, newValue, disableWordDiff, compareMethod, linesOffset, this.props.alwaysShowLines, shouldDeferWordDiff);
258
+ const extraLines = this.props.extraLinesSurroundingDiff < 0
259
+ ? 0
260
+ : Math.round(this.props.extraLinesSurroundingDiff);
261
+ const { lineBlocks, blocks } = computeHiddenBlocks(lineInformation, diffLines, extraLines);
262
+ this.state.computedDiffResult[cacheKey] = { lineInformation, lineBlocks, blocks };
263
+ this.setState((prev) => (Object.assign(Object.assign({}, prev), { computedDiffResult: this.state.computedDiffResult, isLoading: false })), () => {
264
+ // Trigger offset recalculation after diff is computed and rendered
265
+ // Use requestAnimationFrame to ensure DOM is ready for measurement
266
+ if (this.props.infiniteLoading) {
267
+ requestAnimationFrame(() => this.recalculateOffsets());
268
+ }
269
+ });
270
+ });
271
+ /**
272
+ * Handles scroll events on the scrollable container.
273
+ *
274
+ * Updates the visible start row for virtualization.
275
+ */
276
+ this.onScroll = () => {
277
+ const container = this.state.scrollableContainerRef.current;
278
+ if (!container || !this.props.infiniteLoading)
279
+ return;
280
+ // Account for sticky header height in scroll calculations
281
+ const headerHeight = this.getStickyHeaderHeight();
282
+ const contentScrollTop = Math.max(0, container.scrollTop - headerHeight);
283
+ const { cumulativeOffsets } = this.state;
284
+ const newStartRow = cumulativeOffsets
285
+ ? this.findLineAtOffset(contentScrollTop, cumulativeOffsets)
286
+ : Math.floor(contentScrollTop / DiffViewer.ESTIMATED_ROW_HEIGHT);
287
+ // Only update state if the start row changed (avoid unnecessary re-renders)
288
+ if (newStartRow !== this.state.visibleStartRow) {
289
+ this.setState({ visibleStartRow: newStartRow });
290
+ }
291
+ };
292
+ /**
293
+ * Generates the entire diff view with virtualization support.
294
+ */
295
+ this.renderDiff = () => {
296
+ var _a, _b, _c, _d, _e, _f, _g, _h;
297
+ const { splitView, infiniteLoading, showDiffOnly } = this.props;
298
+ const { computedDiffResult, expandedBlocks, visibleStartRow, scrollableContainerRef, cumulativeOffsets } = this.state;
299
+ const cacheKey = this.getMemoisedKey();
300
+ const { lineInformation = [], lineBlocks = [], blocks = [] } = (_a = computedDiffResult[cacheKey]) !== null && _a !== void 0 ? _a : {};
301
+ // Build Set for O(1) comment line lookups
302
+ const commentLineIdsSet = this.getCommentLineIdsSet(this.props.commentLineIds);
303
+ const hasComments = commentLineIdsSet.size > 0 && !!this.props.renderComment;
304
+ const hasRenderGutter = !!this.props.renderGutter;
305
+ // Build Set for O(1) highlight line lookups
306
+ const highlightLinesSet = this.getHighlightLinesSet(this.props.highlightLines);
307
+ // Calculate visible range for virtualization
308
+ let visibleRowStart = 0;
309
+ let visibleRowEnd = Infinity;
310
+ const buffer = 5; // render extra rows above/below viewport
311
+ if (infiniteLoading && scrollableContainerRef.current) {
312
+ const container = scrollableContainerRef.current;
313
+ // Account for sticky header height in scroll calculations
314
+ const headerHeight = this.getStickyHeaderHeight();
315
+ const contentScrollTop = Math.max(0, container.scrollTop - headerHeight);
316
+ if (cumulativeOffsets) {
317
+ // Variable height mode: use binary search to find visible range
318
+ const totalHeight = cumulativeOffsets[cumulativeOffsets.length - 1] || 0;
319
+ const lastRowIndex = cumulativeOffsets.length - 2;
320
+ visibleRowStart = Math.max(0, this.findLineAtOffset(contentScrollTop, cumulativeOffsets) - buffer);
321
+ visibleRowEnd = this.findLineAtOffset(contentScrollTop + container.clientHeight, cumulativeOffsets) + buffer;
322
+ // IMPORTANT: The calculated offsets may overestimate row heights (based on char count),
323
+ // but actual CSS rendering might produce shorter rows. To prevent empty space,
324
+ // ensure we render at least enough rows to fill the viewport using ESTIMATED_ROW_HEIGHT
325
+ // as a conservative minimum.
326
+ const minRowsToFillViewport = Math.ceil(container.clientHeight / DiffViewer.ESTIMATED_ROW_HEIGHT);
327
+ visibleRowEnd = Math.max(visibleRowEnd, visibleRowStart + minRowsToFillViewport + buffer);
328
+ // Also ensure we render all rows when near the bottom
329
+ if (contentScrollTop + container.clientHeight >= totalHeight - buffer * DiffViewer.ESTIMATED_ROW_HEIGHT) {
330
+ visibleRowEnd = lastRowIndex + buffer;
331
+ }
332
+ }
333
+ else {
334
+ // Fixed height fallback
335
+ const viewportRows = Math.ceil(container.clientHeight / DiffViewer.ESTIMATED_ROW_HEIGHT);
336
+ visibleRowStart = Math.max(0, visibleStartRow - buffer);
337
+ visibleRowEnd = visibleStartRow + viewportRows + buffer;
338
+ }
339
+ }
340
+ // First pass: build a map of lineIndex -> renderedRowIndex
341
+ // This accounts for code folding where some lines don't render or render as fold indicators
342
+ const lineToRowMap = new Map();
343
+ const seenBlocks = new Set();
344
+ let currentRow = 0;
345
+ for (let i = 0; i < lineInformation.length; i++) {
346
+ const blockIndex = lineBlocks[i];
347
+ if (showDiffOnly && blockIndex !== undefined) {
348
+ if (!expandedBlocks.includes(blockIndex)) {
349
+ // Line is in a collapsed block
350
+ const lastLineOfBlock = blocks[blockIndex].endLine === i;
351
+ if (!seenBlocks.has(blockIndex) && lastLineOfBlock) {
352
+ // This line renders as a fold indicator
353
+ seenBlocks.add(blockIndex);
354
+ lineToRowMap.set(i, currentRow);
355
+ currentRow++;
356
+ }
357
+ // Other lines in collapsed block don't render
358
+ }
359
+ else {
360
+ // Block is expanded, line renders normally
361
+ lineToRowMap.set(i, currentRow);
362
+ currentRow++;
363
+ }
364
+ }
365
+ else {
366
+ // Not in a block or showDiffOnly is false, line renders normally
367
+ lineToRowMap.set(i, currentRow);
368
+ currentRow++;
369
+ }
370
+ }
371
+ const totalRenderedRows = currentRow;
372
+ // Second pass: render only lines in the visible range
373
+ const diffNodes = [];
374
+ let topPadding = 0;
375
+ let firstVisibleFound = false;
376
+ let lastRenderedRowIndex = -1;
377
+ seenBlocks.clear();
378
+ for (let lineIndex = 0; lineIndex < lineInformation.length; lineIndex++) {
379
+ const line = lineInformation[lineIndex];
380
+ const rowIndex = lineToRowMap.get(lineIndex);
381
+ // Skip lines that don't render (hidden in collapsed blocks)
382
+ if (rowIndex === undefined)
383
+ continue;
384
+ // Skip lines before visible range
385
+ if (rowIndex < visibleRowStart) {
386
+ continue;
387
+ }
388
+ // Stop after visible range
389
+ if (rowIndex > visibleRowEnd) {
390
+ break;
391
+ }
392
+ // Calculate top padding from the first visible row
393
+ if (!firstVisibleFound) {
394
+ topPadding = cumulativeOffsets
395
+ ? cumulativeOffsets[rowIndex] || 0
396
+ : rowIndex * DiffViewer.ESTIMATED_ROW_HEIGHT;
397
+ firstVisibleFound = true;
398
+ }
399
+ // Track the last rendered row for bottom padding calculation
400
+ lastRenderedRowIndex = rowIndex;
401
+ // Render the line
402
+ if (showDiffOnly) {
403
+ const blockIndex = lineBlocks[lineIndex];
404
+ if (blockIndex !== undefined) {
405
+ const lastLineOfBlock = blocks[blockIndex].endLine === lineIndex;
406
+ if (!expandedBlocks.includes(blockIndex) &&
407
+ lastLineOfBlock) {
408
+ diffNodes.push(_jsx(SkippedLineIndicator, { num: blocks[blockIndex].lines, blockNumber: blockIndex, leftBlockLineNumber: line.left.lineNumber, rightBlockLineNumber: line.right.lineNumber, hideLineNumbers: this.props.hideLineNumbers, splitView: this.props.splitView, styles: this.styles, onBlockClick: this.onBlockExpand, codeFoldMessageRenderer: this.props.codeFoldMessageRenderer, renderGutter: this.props.renderGutter }, `fold-${lineIndex}`));
409
+ continue;
410
+ }
411
+ if (!expandedBlocks.includes(blockIndex)) {
412
+ continue;
413
+ }
414
+ }
415
+ }
416
+ // Compute word diff on-demand if deferred
417
+ const { leftValue, rightValue } = this.getWordDiffValues(line.left, line.right, lineIndex);
418
+ diffNodes.push(_jsx(DiffRow, { index: lineIndex, leftLineNumber: line.left.lineNumber, leftType: line.left.type, leftValue: leftValue, rightLineNumber: line.right.lineNumber, rightType: line.right.type, rightValue: rightValue, highlightLeft: highlightLinesSet.has(`L-${line.left.lineNumber}`), highlightRight: highlightLinesSet.has(`R-${line.right.lineNumber}`), splitView: splitView, hideLineNumbers: this.props.hideLineNumbers, styles: this.styles, onLineNumberClick: this.props.onLineNumberClick, renderContent: this.props.renderContent, renderGutter: this.props.renderGutter, compareMethod: this.props.compareMethod, contentColumnRef: this.contentColumnRef, hasCumulativeOffsets: !!cumulativeOffsets }, lineIndex));
419
+ // Inject comment rows after the DiffRow
420
+ if (hasComments) {
421
+ const leftLineId = ((_b = line.left) === null || _b === void 0 ? void 0 : _b.lineNumber) ? `L-${line.left.lineNumber}` : null;
422
+ const rightLineId = ((_c = line.right) === null || _c === void 0 ? void 0 : _c.lineNumber) ? `R-${line.right.lineNumber}` : null;
423
+ if (leftLineId && commentLineIdsSet.has(leftLineId)) {
424
+ diffNodes.push(_jsx(CommentRow, { lineId: leftLineId, lineNumber: line.left.lineNumber, prefix: LineNumberPrefix.LEFT, splitView: splitView, hideLineNumbers: this.props.hideLineNumbers, hasRenderGutter: hasRenderGutter, styles: this.styles, renderComment: this.props.renderComment, trRef: this.getCommentRowRef(leftLineId) }, `comment-${leftLineId}`));
425
+ }
426
+ if (rightLineId && rightLineId !== leftLineId && commentLineIdsSet.has(rightLineId)) {
427
+ diffNodes.push(_jsx(CommentRow, { lineId: rightLineId, lineNumber: line.right.lineNumber, prefix: LineNumberPrefix.RIGHT, splitView: splitView, hideLineNumbers: this.props.hideLineNumbers, hasRenderGutter: hasRenderGutter, styles: this.styles, renderComment: this.props.renderComment, trRef: this.getCommentRowRef(rightLineId) }, `comment-${rightLineId}`));
428
+ }
429
+ }
430
+ }
431
+ // Calculate total content height
432
+ const totalContentHeight = cumulativeOffsets
433
+ ? cumulativeOffsets[cumulativeOffsets.length - 1] || 0
434
+ : totalRenderedRows * DiffViewer.ESTIMATED_ROW_HEIGHT;
435
+ // Calculate bottom padding: space after the last rendered row
436
+ const bottomPadding = cumulativeOffsets && lastRenderedRowIndex >= 0
437
+ ? totalContentHeight - (cumulativeOffsets[lastRenderedRowIndex + 1] || totalContentHeight)
438
+ : 0;
439
+ return {
440
+ diffNodes,
441
+ blocks,
442
+ lineInformation,
443
+ totalRenderedRows,
444
+ topPadding,
445
+ bottomPadding,
446
+ totalContentHeight,
447
+ renderedCount: diffNodes.length,
448
+ // Debug info
449
+ debug: {
450
+ visibleRowStart,
451
+ visibleRowEnd,
452
+ totalRows: totalRenderedRows,
453
+ offsetsLength: (_d = cumulativeOffsets === null || cumulativeOffsets === void 0 ? void 0 : cumulativeOffsets.length) !== null && _d !== void 0 ? _d : 0,
454
+ renderedCount: diffNodes.length,
455
+ scrollTop: (_f = (_e = scrollableContainerRef.current) === null || _e === void 0 ? void 0 : _e.scrollTop) !== null && _f !== void 0 ? _f : 0,
456
+ headerHeight: this.getStickyHeaderHeight(),
457
+ contentScrollTop: scrollableContainerRef.current
458
+ ? Math.max(0, scrollableContainerRef.current.scrollTop - this.getStickyHeaderHeight())
459
+ : 0,
460
+ clientHeight: (_h = (_g = scrollableContainerRef.current) === null || _g === void 0 ? void 0 : _g.clientHeight) !== null && _h !== void 0 ? _h : 0,
461
+ }
462
+ };
463
+ };
464
+ this.render = () => {
465
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
466
+ const { oldValue, newValue, useDarkTheme, leftTitle, rightTitle, splitView, compareMethod, hideLineNumbers, nonce, } = this.props;
467
+ if (typeof compareMethod === "string" &&
468
+ compareMethod !== DiffMethod.JSON) {
469
+ if (typeof oldValue !== "string" || typeof newValue !== "string") {
470
+ throw Error('"oldValue" and "newValue" should be strings');
471
+ }
472
+ }
473
+ this.styles = this.computeStyles(this.props.styles, useDarkTheme, nonce);
474
+ const nodes = this.renderDiff();
475
+ let colSpanOnSplitView = 3;
476
+ let colSpanOnInlineView = 4;
477
+ if (hideLineNumbers) {
478
+ colSpanOnSplitView -= 1;
479
+ colSpanOnInlineView -= 1;
480
+ }
481
+ if (this.props.renderGutter) {
482
+ colSpanOnSplitView += 1;
483
+ colSpanOnInlineView += 1;
484
+ }
485
+ let deletions = 0;
486
+ let additions = 0;
487
+ for (const l of nodes.lineInformation) {
488
+ if (l.left.type === DiffType.ADDED) {
489
+ additions++;
490
+ }
491
+ if (l.right.type === DiffType.ADDED) {
492
+ additions++;
493
+ }
494
+ if (l.left.type === DiffType.REMOVED) {
495
+ deletions++;
496
+ }
497
+ if (l.right.type === DiffType.REMOVED) {
498
+ deletions++;
499
+ }
500
+ }
501
+ const totalChanges = deletions + additions;
502
+ const percentageAddition = Math.round((additions / totalChanges) * 100);
503
+ const blocks = [];
504
+ for (let i = 0; i < 5; i++) {
505
+ if (percentageAddition > i * 20) {
506
+ blocks.push(_jsx("span", { className: cn(this.styles.block, this.styles.blockAddition) }, i));
507
+ }
508
+ else {
509
+ blocks.push(_jsx("span", { className: cn(this.styles.block, this.styles.blockDeletion) }, i));
510
+ }
511
+ }
512
+ const allExpanded = this.state.expandedBlocks.length === nodes.blocks.length;
513
+ const LoadingElement = this.props.loadingElement;
514
+ const scrollDivStyle = this.props.infiniteLoading ? {
515
+ overflowY: 'scroll',
516
+ overflowX: 'hidden',
517
+ height: this.props.infiniteLoading.containerHeight
518
+ } : {};
519
+ // Only apply noWrap when infiniteLoading is enabled but we don't have cumulative offsets yet
520
+ // Once offsets are calculated, we enable pre-wrap for proper text wrapping
521
+ const shouldNoWrap = !!this.props.infiniteLoading && !this.state.cumulativeOffsets;
522
+ const tableElement = (_jsxs("table", { className: cn(this.styles.diffContainer, {
523
+ [this.styles.splitView]: splitView,
524
+ [this.styles.noWrap]: shouldNoWrap,
525
+ }), onMouseUp: () => {
526
+ const elements = document.getElementsByClassName("right");
527
+ for (let i = 0; i < elements.length; i++) {
528
+ const element = elements.item(i);
529
+ element.classList.remove(this.styles.noSelect);
530
+ }
531
+ const elementsLeft = document.getElementsByClassName("left");
532
+ for (let i = 0; i < elementsLeft.length; i++) {
533
+ const element = elementsLeft.item(i);
534
+ element.classList.remove(this.styles.noSelect);
535
+ }
536
+ }, children: [_jsxs("colgroup", { children: [!this.props.hideLineNumbers && _jsx("col", { width: "50px" }), !splitView && !this.props.hideLineNumbers && _jsx("col", { width: "50px" }), this.props.renderGutter && _jsx("col", { width: "50px" }), _jsx("col", { width: "28px" }), _jsx("col", { width: "auto" }), splitView && (_jsxs(_Fragment, { children: [!this.props.hideLineNumbers && _jsx("col", { width: "50px" }), this.props.renderGutter && _jsx("col", { width: "50px" }), _jsx("col", { width: "28px" }), _jsx("col", { width: "auto" })] }))] }), _jsx("tbody", { children: nodes.diffNodes })] }));
537
+ return (_jsxs("div", { style: Object.assign(Object.assign({}, scrollDivStyle), { position: 'relative' }), onScroll: this.onScroll, ref: this.state.scrollableContainerRef, children: [(!this.props.hideSummary || leftTitle || rightTitle) && (_jsxs("div", { ref: this.stickyHeaderRef, className: this.styles.stickyHeader, children: [!this.props.hideSummary && (_jsxs("div", { className: this.styles.summary, role: "banner", children: [_jsx("button", { type: "button", className: this.styles.allExpandButton, onClick: () => {
538
+ this.setState({
539
+ expandedBlocks: allExpanded
540
+ ? []
541
+ : nodes.blocks.map((b) => b.index),
542
+ }, () => this.recalculateOffsets());
543
+ }, children: allExpanded ? _jsx(Fold, {}) : _jsx(Expand, {}) }), " ", totalChanges, _jsx("div", { style: { display: "flex", gap: "1px" }, children: blocks }), this.props.summary ? _jsx("span", { children: this.props.summary }) : null] })), (leftTitle || rightTitle) && (_jsxs("div", { className: this.styles.columnHeaders, children: [_jsx("div", { className: this.styles.titleBlock, children: leftTitle ? (_jsx("pre", { className: this.styles.contentText, children: leftTitle })) : null }), splitView && (_jsx("div", { className: this.styles.titleBlock, children: rightTitle ? (_jsx("pre", { className: this.styles.contentText, children: rightTitle })) : null }))] }))] })), this.state.isLoading && LoadingElement && _jsx(LoadingElement, {}), this.props.infiniteLoading ? (_jsx("div", { style: {
544
+ height: nodes.totalContentHeight,
545
+ position: 'relative',
546
+ }, children: _jsx("div", { style: {
547
+ position: 'absolute',
548
+ top: nodes.topPadding,
549
+ left: 0,
550
+ right: 0,
551
+ }, children: tableElement }) })) : (tableElement), _jsx("span", { ref: this.charMeasureRef, style: {
552
+ position: 'absolute',
553
+ top: 0,
554
+ left: '-9999px',
555
+ visibility: 'hidden',
556
+ whiteSpace: 'pre',
557
+ fontFamily: 'monospace',
558
+ fontSize: 12,
559
+ }, "aria-hidden": "true", children: "M" }), this.props.infiniteLoading && this.props.showDebugInfo && (_jsxs("div", { style: {
560
+ position: 'fixed',
561
+ top: 10,
562
+ right: 10,
563
+ background: 'rgba(0,0,0,0.85)',
564
+ color: '#0f0',
565
+ padding: '10px',
566
+ fontFamily: 'monospace',
567
+ fontSize: '11px',
568
+ zIndex: 9999,
569
+ borderRadius: '4px',
570
+ maxWidth: '300px',
571
+ lineHeight: 1.4,
572
+ }, children: [_jsx("div", { style: { fontWeight: 'bold', marginBottom: '5px', color: '#fff' }, children: "Debug Info" }), _jsxs("div", { children: ["scrollTop: ", nodes.debug.scrollTop] }), _jsxs("div", { children: ["headerHeight: ", nodes.debug.headerHeight] }), _jsxs("div", { children: ["contentScrollTop: ", nodes.debug.contentScrollTop] }), _jsxs("div", { children: ["clientHeight: ", nodes.debug.clientHeight] }), _jsxs("div", { style: { marginTop: '5px', borderTop: '1px solid #444', paddingTop: '5px' }, children: [_jsxs("div", { children: ["visibleRowStart: ", nodes.debug.visibleRowStart] }), _jsxs("div", { children: ["visibleRowEnd: ", nodes.debug.visibleRowEnd] })] }), _jsxs("div", { style: { marginTop: '5px', borderTop: '1px solid #444', paddingTop: '5px' }, children: [_jsxs("div", { children: ["totalRows: ", nodes.debug.totalRows] }), _jsxs("div", { children: ["offsetsLength: ", nodes.debug.offsetsLength] }), _jsxs("div", { children: ["renderedCount: ", nodes.debug.renderedCount] })] }), _jsxs("div", { style: { marginTop: '5px', borderTop: '1px solid #444', paddingTop: '5px' }, children: [_jsxs("div", { children: ["topPadding: ", nodes.topPadding.toFixed(0)] }), _jsxs("div", { children: ["bottomPadding: ", nodes.bottomPadding.toFixed(0)] }), _jsxs("div", { children: ["totalContentHeight: ", nodes.totalContentHeight.toFixed(0)] })] }), _jsxs("div", { style: { marginTop: '5px', borderTop: '1px solid #444', paddingTop: '5px', color: '#ff0' }, children: [_jsxs("div", { children: ["cumulativeOffsets: ", this.state.cumulativeOffsets ? 'SET' : 'NULL'] }), _jsxs("div", { children: ["columnWidth: ", (_b = (_a = this.state.contentColumnWidth) === null || _a === void 0 ? void 0 : _a.toFixed(0)) !== null && _b !== void 0 ? _b : 'N/A', "px"] }), _jsxs("div", { children: ["charWidth: ", (_d = (_c = this.state.charWidth) === null || _c === void 0 ? void 0 : _c.toFixed(2)) !== null && _d !== void 0 ? _d : 'N/A', "px"] }), _jsxs("div", { children: ["charsPerRow: ", this.state.contentColumnWidth && this.state.charWidth ? Math.floor(this.state.contentColumnWidth / this.state.charWidth) : 'N/A'] })] }), this.state.cumulativeOffsets && (_jsxs("div", { style: { marginTop: '5px', borderTop: '1px solid #444', paddingTop: '5px', color: '#0ff', fontSize: '10px' }, children: [_jsxs("div", { children: ["offsets[", nodes.debug.visibleRowEnd, "]: ", (_f = (_e = this.state.cumulativeOffsets[nodes.debug.visibleRowEnd]) === null || _e === void 0 ? void 0 : _e.toFixed(0)) !== null && _f !== void 0 ? _f : 'N/A'] }), _jsxs("div", { children: ["offsets[", nodes.debug.totalRows - 1, "]: ", (_h = (_g = this.state.cumulativeOffsets[nodes.debug.totalRows - 1]) === null || _g === void 0 ? void 0 : _g.toFixed(0)) !== null && _h !== void 0 ? _h : 'N/A'] }), _jsxs("div", { children: ["offsets[", nodes.debug.totalRows, "]: ", (_k = (_j = this.state.cumulativeOffsets[nodes.debug.totalRows]) === null || _j === void 0 ? void 0 : _j.toFixed(0)) !== null && _k !== void 0 ? _k : 'N/A'] }), _jsxs("div", { style: { marginTop: '3px' }, children: ["viewportEnd: ", (nodes.debug.contentScrollTop + nodes.debug.clientHeight).toFixed(0)] }), _jsxs("div", { style: { marginTop: '3px', color: '#f0f' }, children: ["scrollHeight: ", (_m = (_l = this.state.scrollableContainerRef.current) === null || _l === void 0 ? void 0 : _l.scrollHeight) !== null && _m !== void 0 ? _m : 'N/A'] }), _jsxs("div", { children: ["maxScrollTop: ", ((_p = (_o = this.state.scrollableContainerRef.current) === null || _o === void 0 ? void 0 : _o.scrollHeight) !== null && _p !== void 0 ? _p : 0) - nodes.debug.clientHeight] })] }))] }))] }));
573
+ };
574
+ this.state = {
575
+ expandedBlocks: [],
576
+ noSelect: undefined,
577
+ scrollableContainerRef: React.createRef(),
578
+ computedDiffResult: {},
579
+ isLoading: false,
580
+ visibleStartRow: 0,
581
+ contentColumnWidth: null,
582
+ charWidth: null,
583
+ cumulativeOffsets: null,
584
+ };
585
+ }
586
+ /**
587
+ * Gets the height of the sticky header, if present.
588
+ */
589
+ getStickyHeaderHeight() {
590
+ var _a;
591
+ return ((_a = this.stickyHeaderRef.current) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 0;
592
+ }
593
+ /**
594
+ * Measures the width of a single character in the monospace font.
595
+ * Falls back to 7.2px if measurement fails.
596
+ */
597
+ measureCharWidth() {
598
+ const span = this.charMeasureRef.current;
599
+ if (!span)
600
+ return 7.2; // fallback
601
+ return span.getBoundingClientRect().width || 7.2;
602
+ }
603
+ /**
604
+ * Measures the available width for content in a content column.
605
+ * Falls back to estimating from container width if direct measurement fails.
606
+ */
607
+ measureContentColumnWidth() {
608
+ // Try direct measurement first
609
+ const cell = this.contentColumnRef.current;
610
+ if (cell && cell.clientWidth > 0) {
611
+ const style = window.getComputedStyle(cell);
612
+ const padding = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
613
+ const width = cell.clientWidth - padding;
614
+ if (width > 0)
615
+ return width;
616
+ }
617
+ // Fallback: estimate from container width
618
+ // In split view: container has 2 content columns + gutters (50px each) + markers (28px each)
619
+ // In unified view: 1 content column + 2 gutters + 1 marker
620
+ const container = this.state.scrollableContainerRef.current;
621
+ if (!container || container.clientWidth <= 0)
622
+ return null;
623
+ const containerWidth = container.clientWidth;
624
+ const gutterWidth = this.props.hideLineNumbers ? 0 : 50;
625
+ const markerWidth = 28;
626
+ const gutterCount = this.props.splitView ? 2 : 2; // left gutter(s)
627
+ const markerCount = this.props.splitView ? 2 : 1;
628
+ const contentColumns = this.props.splitView ? 2 : 1;
629
+ const fixedWidth = gutterCount * gutterWidth + markerCount * markerWidth;
630
+ const availableWidth = containerWidth - fixedWidth;
631
+ return Math.max(100, availableWidth / contentColumns); // minimum 100px
632
+ }
633
+ /**
634
+ * Gets the text length from a value that may be a string or DiffInformation array.
635
+ */
636
+ getTextLength(value) {
637
+ if (!value)
638
+ return 0;
639
+ if (typeof value === 'string')
640
+ return value.length;
641
+ return value.reduce((sum, d) => sum + (typeof d.value === 'string' ? d.value.length : 0), 0);
642
+ }
643
+ /**
644
+ * Builds cumulative vertical offsets for each line based on character count and column width.
645
+ * This allows accurate scroll position calculations with variable row heights.
646
+ */
647
+ buildCumulativeOffsets(lineInformation, lineBlocks, blocks, expandedBlocks, showDiffOnly, charWidth, columnWidth, splitView, commentLineIdsSet) {
648
+ var _a, _b, _c, _d, _e, _f, _g;
649
+ const offsets = [0];
650
+ const seenBlocks = new Set();
651
+ const estimatedCommentHeight = (_a = this.props.estimatedCommentRowHeight) !== null && _a !== void 0 ? _a : DiffViewer.ESTIMATED_COMMENT_ROW_HEIGHT;
652
+ for (let i = 0; i < lineInformation.length; i++) {
653
+ const line = lineInformation[i];
654
+ if (showDiffOnly) {
655
+ const blockIndex = lineBlocks[i];
656
+ if (blockIndex !== undefined && !expandedBlocks.includes(blockIndex)) {
657
+ const isLastLine = blocks[blockIndex].endLine === i;
658
+ if (!seenBlocks.has(blockIndex) && isLastLine) {
659
+ seenBlocks.add(blockIndex);
660
+ offsets.push(offsets[offsets.length - 1] + DiffViewer.ESTIMATED_ROW_HEIGHT);
661
+ }
662
+ continue;
663
+ }
664
+ }
665
+ // Calculate visual rows for this line
666
+ const leftLen = ((_b = line.left) === null || _b === void 0 ? void 0 : _b.value) ? this.getTextLength(line.left.value) : 0;
667
+ const rightLen = ((_c = line.right) === null || _c === void 0 ? void 0 : _c.value) ? this.getTextLength(line.right.value) : 0;
668
+ const maxLen = splitView ? Math.max(leftLen, rightLen) : (leftLen || rightLen);
669
+ const charsPerRow = Math.floor(columnWidth / charWidth);
670
+ const visualRows = charsPerRow > 0 ? Math.max(1, Math.ceil(maxLen / charsPerRow)) : 1;
671
+ let lineHeight = visualRows * DiffViewer.ESTIMATED_ROW_HEIGHT;
672
+ // Add height for comment rows on this line
673
+ if (commentLineIdsSet && commentLineIdsSet.size > 0) {
674
+ const leftId = ((_d = line.left) === null || _d === void 0 ? void 0 : _d.lineNumber) ? `L-${line.left.lineNumber}` : null;
675
+ const rightId = ((_e = line.right) === null || _e === void 0 ? void 0 : _e.lineNumber) ? `R-${line.right.lineNumber}` : null;
676
+ if (leftId && commentLineIdsSet.has(leftId)) {
677
+ lineHeight += (_f = this.commentRowHeights.get(leftId)) !== null && _f !== void 0 ? _f : estimatedCommentHeight;
678
+ }
679
+ if (rightId && rightId !== leftId && commentLineIdsSet.has(rightId)) {
680
+ lineHeight += (_g = this.commentRowHeights.get(rightId)) !== null && _g !== void 0 ? _g : estimatedCommentHeight;
681
+ }
682
+ }
683
+ offsets.push(offsets[offsets.length - 1] + lineHeight);
684
+ }
685
+ return offsets;
686
+ }
687
+ /**
688
+ * Binary search to find the line index at a given scroll offset.
689
+ */
690
+ findLineAtOffset(scrollTop, offsets) {
691
+ let low = 0;
692
+ let high = offsets.length - 2;
693
+ while (low < high) {
694
+ const mid = Math.floor((low + high + 1) / 2);
695
+ if (offsets[mid] <= scrollTop) {
696
+ low = mid;
697
+ }
698
+ else {
699
+ high = mid - 1;
700
+ }
701
+ }
702
+ return low;
703
+ }
704
+ componentDidUpdate(prevProps) {
705
+ var _a;
706
+ if (prevProps.oldValue !== this.props.oldValue ||
707
+ prevProps.newValue !== this.props.newValue ||
708
+ prevProps.compareMethod !== this.props.compareMethod ||
709
+ prevProps.disableWordDiff !== this.props.disableWordDiff ||
710
+ prevProps.linesOffset !== this.props.linesOffset) {
711
+ // Clear word diff cache when diff changes
712
+ this.wordDiffCache.clear();
713
+ // Reset scroll position to top
714
+ const container = this.state.scrollableContainerRef.current;
715
+ if (container) {
716
+ container.scrollTop = 0;
717
+ }
718
+ this.setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: true, visibleStartRow: 0, cumulativeOffsets: null })));
719
+ this.memoisedCompute();
720
+ }
721
+ // Recalculate offsets when commentLineIds change
722
+ if (!DiffViewer.shallowArrayEqual(prevProps.commentLineIds, this.props.commentLineIds)) {
723
+ // Clean stale entries from height measurement maps and ref cache
724
+ const currentSet = this.getCommentLineIdsSet(this.props.commentLineIds);
725
+ for (const lineId of this.commentRowHeights.keys()) {
726
+ if (!currentSet.has(lineId)) {
727
+ this.commentRowHeights.delete(lineId);
728
+ this.commentRowRefCache.delete(lineId);
729
+ const el = this.commentRowElements.get(lineId);
730
+ if (el) {
731
+ (_a = this.commentRowObserver) === null || _a === void 0 ? void 0 : _a.unobserve(el);
732
+ this.commentRowElements.delete(lineId);
733
+ }
734
+ }
735
+ }
736
+ if (this.props.infiniteLoading) {
737
+ this.scheduleOffsetRecalc();
738
+ }
739
+ }
740
+ }
741
+ componentDidMount() {
742
+ this.setState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: true })));
743
+ this.memoisedCompute();
744
+ // Set up ResizeObserver for recalculating offsets on container resize
745
+ if (typeof ResizeObserver !== 'undefined' && this.props.infiniteLoading) {
746
+ this.resizeObserver = new ResizeObserver(() => {
747
+ requestAnimationFrame(() => this.recalculateOffsets());
748
+ });
749
+ const container = this.state.scrollableContainerRef.current;
750
+ if (container) {
751
+ this.resizeObserver.observe(container);
752
+ }
753
+ }
754
+ // Initialize comment row observer for height measurement
755
+ this.initCommentRowObserver();
756
+ }
757
+ componentWillUnmount() {
758
+ var _a, _b;
759
+ (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
760
+ (_b = this.commentRowObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
761
+ }
762
+ }
763
+ DiffViewer.ESTIMATED_COMMENT_ROW_HEIGHT = 100;
764
+ DiffViewer.defaultProps = {
765
+ oldValue: "",
766
+ newValue: "",
767
+ splitView: true,
768
+ highlightLines: [],
769
+ disableWordDiff: false,
770
+ compareMethod: DiffMethod.CHARS,
771
+ styles: {},
772
+ hideLineNumbers: false,
773
+ extraLinesSurroundingDiff: 3,
774
+ showDiffOnly: true,
775
+ useDarkTheme: false,
776
+ linesOffset: 0,
777
+ nonce: "",
778
+ };
779
+ // Estimated row height based on lineHeight: 1.6em with 12px base font
780
+ DiffViewer.ESTIMATED_ROW_HEIGHT = 19;
781
+ export default DiffViewer;
782
+ export { DiffMethod };
783
+ export { default as computeStyles } from "./styles.js";