@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,5 @@
1
+ export var LineNumberPrefix;
2
+ (function (LineNumberPrefix) {
3
+ LineNumberPrefix["LEFT"] = "L";
4
+ LineNumberPrefix["RIGHT"] = "R";
5
+ })(LineNumberPrefix || (LineNumberPrefix = {}));
@@ -0,0 +1,211 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import cn from "classnames";
3
+ import * as React from "react";
4
+ import { DiffMethod, DiffType, } from "./compute-lines.js";
5
+ /**
6
+ * Applies diff styling (ins/del tags) to pre-highlighted HTML by walking through
7
+ * the HTML and wrapping text portions based on character positions in the diff.
8
+ */
9
+ export function applyDiffToHighlightedHtml(html, diffArray, styles) {
10
+ const ranges = [];
11
+ let pos = 0;
12
+ for (const diff of diffArray) {
13
+ const value = typeof diff.value === "string" ? diff.value : "";
14
+ if (value.length > 0) {
15
+ ranges.push({ start: pos, end: pos + value.length, type: diff.type });
16
+ pos += value.length;
17
+ }
18
+ }
19
+ const segments = [];
20
+ let i = 0;
21
+ while (i < html.length) {
22
+ if (html[i] === "<") {
23
+ const tagEnd = html.indexOf(">", i);
24
+ if (tagEnd === -1) {
25
+ // Malformed HTML, treat rest as text
26
+ segments.push({ type: "text", content: html.slice(i) });
27
+ break;
28
+ }
29
+ segments.push({ type: "tag", content: html.slice(i, tagEnd + 1) });
30
+ i = tagEnd + 1;
31
+ }
32
+ else {
33
+ // Find the next tag or end of string
34
+ let textEnd = html.indexOf("<", i);
35
+ if (textEnd === -1)
36
+ textEnd = html.length;
37
+ segments.push({ type: "text", content: html.slice(i, textEnd) });
38
+ i = textEnd;
39
+ }
40
+ }
41
+ // Helper to decode HTML entities for character counting
42
+ function decodeEntities(text) {
43
+ return text
44
+ .replace(/&lt;/g, "<")
45
+ .replace(/&gt;/g, ">")
46
+ .replace(/&amp;/g, "&")
47
+ .replace(/&quot;/g, '"')
48
+ .replace(/&#39;/g, "'")
49
+ .replace(/&#x27;/g, "'")
50
+ .replace(/&nbsp;/g, "\u00A0");
51
+ }
52
+ // Helper to get the wrapper tag for a diff type
53
+ function getWrapper(type) {
54
+ if (type === DiffType.ADDED) {
55
+ return {
56
+ open: `<ins class="${styles.wordDiff} ${styles.wordAdded}">`,
57
+ close: "</ins>",
58
+ };
59
+ }
60
+ if (type === DiffType.REMOVED) {
61
+ return {
62
+ open: `<del class="${styles.wordDiff} ${styles.wordRemoved}">`,
63
+ close: "</del>",
64
+ };
65
+ }
66
+ return {
67
+ open: `<span class="${styles.wordDiff}">`,
68
+ close: "</span>",
69
+ };
70
+ }
71
+ // Process segments, tracking text position
72
+ let textPos = 0;
73
+ let result = "";
74
+ for (const segment of segments) {
75
+ if (segment.type === "tag") {
76
+ result += segment.content;
77
+ }
78
+ else {
79
+ // Text segment - we need to split it according to diff ranges
80
+ const text = segment.content;
81
+ const decodedText = decodeEntities(text);
82
+ // Walk through the text, character by character (in decoded form)
83
+ // but output the original encoded form
84
+ let localDecodedPos = 0;
85
+ let localEncodedPos = 0;
86
+ while (localDecodedPos < decodedText.length) {
87
+ const globalPos = textPos + localDecodedPos;
88
+ // Find the range that covers this position
89
+ const range = ranges.find((r) => globalPos >= r.start && globalPos < r.end);
90
+ if (!range) {
91
+ // No range covers this position (shouldn't happen, but be safe)
92
+ // Just output the character
93
+ const char = text[localEncodedPos];
94
+ result += char;
95
+ localEncodedPos++;
96
+ localDecodedPos++;
97
+ continue;
98
+ }
99
+ // How many decoded characters until the end of this range?
100
+ const charsUntilRangeEnd = range.end - globalPos;
101
+ // How many decoded characters until the end of this text segment?
102
+ const charsUntilTextEnd = decodedText.length - localDecodedPos;
103
+ // Take the minimum
104
+ const charsToTake = Math.min(charsUntilRangeEnd, charsUntilTextEnd);
105
+ // Now we need to find the corresponding encoded substring
106
+ // Walk through encoded text, counting decoded characters
107
+ let encodedChunkEnd = localEncodedPos;
108
+ let decodedCount = 0;
109
+ while (decodedCount < charsToTake && encodedChunkEnd < text.length) {
110
+ if (text[encodedChunkEnd] === "&") {
111
+ // Find entity end
112
+ const entityEnd = text.indexOf(";", encodedChunkEnd);
113
+ if (entityEnd !== -1 && entityEnd - encodedChunkEnd < 10) {
114
+ encodedChunkEnd = entityEnd + 1;
115
+ }
116
+ else {
117
+ encodedChunkEnd++;
118
+ }
119
+ }
120
+ else {
121
+ encodedChunkEnd++;
122
+ }
123
+ decodedCount++;
124
+ }
125
+ const chunk = text.slice(localEncodedPos, encodedChunkEnd);
126
+ const wrapper = getWrapper(range.type);
127
+ if (wrapper) {
128
+ result += wrapper.open + chunk + wrapper.close;
129
+ }
130
+ else {
131
+ result += chunk;
132
+ }
133
+ localEncodedPos = encodedChunkEnd;
134
+ localDecodedPos += charsToTake;
135
+ }
136
+ textPos += decodedText.length;
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ /**
142
+ * Checks if the current compare method should show word-level highlighting.
143
+ */
144
+ function shouldHighlightWordDiff(compareMethod) {
145
+ return (compareMethod === DiffMethod.CHARS ||
146
+ compareMethod === DiffMethod.WORDS ||
147
+ compareMethod === DiffMethod.WORDS_WITH_SPACE ||
148
+ compareMethod === DiffMethod.JSON ||
149
+ compareMethod === DiffMethod.YAML);
150
+ }
151
+ /**
152
+ * Maps over the word diff and constructs the required React elements to show word diff.
153
+ *
154
+ * @param diffArray Word diff information derived from line information.
155
+ * @param styles Computed styles for the diff viewer.
156
+ * @param compareMethod The diff comparison method being used.
157
+ * @param renderer Optional renderer to format diff words. Useful for syntax highlighting.
158
+ */
159
+ export function renderWordDiff(diffArray, styles, compareMethod, renderer) {
160
+ const showHighlight = shouldHighlightWordDiff(compareMethod);
161
+ // Reconstruct the full line from diff chunks
162
+ const fullLine = diffArray
163
+ .map((d) => (typeof d.value === "string" ? d.value : ""))
164
+ .join("");
165
+ // For very long lines (>500 chars), skip fancy processing - just render plain text
166
+ // without word-level highlighting to avoid performance issues
167
+ const MAX_LINE_LENGTH = 500;
168
+ if (fullLine.length > MAX_LINE_LENGTH) {
169
+ return [_jsx("span", { children: fullLine }, "long-line")];
170
+ }
171
+ // If we have a renderer, try to highlight the full line first,
172
+ // then apply diff styling to preserve proper tokenization.
173
+ if (renderer) {
174
+ // Get the syntax-highlighted content
175
+ const highlighted = renderer(fullLine);
176
+ // Check if the renderer uses dangerouslySetInnerHTML (common with Prism, highlight.js, etc.)
177
+ const htmlContent = highlighted?.props?.dangerouslySetInnerHTML?.__html;
178
+ if (typeof htmlContent === "string") {
179
+ // Apply diff styling to the highlighted HTML
180
+ const styledHtml = applyDiffToHighlightedHtml(htmlContent, diffArray, {
181
+ wordDiff: styles.wordDiff,
182
+ wordAdded: showHighlight ? styles.wordAdded : "",
183
+ wordRemoved: showHighlight ? styles.wordRemoved : "",
184
+ });
185
+ // Clone the element with the modified HTML
186
+ return [
187
+ React.cloneElement(highlighted, {
188
+ key: "highlighted-diff",
189
+ dangerouslySetInnerHTML: { __html: styledHtml },
190
+ }),
191
+ ];
192
+ }
193
+ // Renderer doesn't use dangerouslySetInnerHTML - fall through to per-chunk rendering
194
+ }
195
+ // Fallback: render each chunk separately (used for JSON/YAML or non-HTML renderers)
196
+ return diffArray.map((wordDiff, i) => {
197
+ let content;
198
+ if (typeof wordDiff.value === "string") {
199
+ content = wordDiff.value;
200
+ }
201
+ else {
202
+ // If wordDiff.value is DiffInformation[], we don't handle it. See c0c99f5712.
203
+ content = undefined;
204
+ }
205
+ return wordDiff.type === DiffType.ADDED ? (_jsx("ins", { className: cn(styles.wordDiff, {
206
+ [styles.wordAdded]: showHighlight,
207
+ }), children: content }, i)) : wordDiff.type === DiffType.REMOVED ? (_jsx("del", { className: cn(styles.wordDiff, {
208
+ [styles.wordRemoved]: showHighlight,
209
+ }), children: content }, i)) : (_jsx("span", { className: cn(styles.wordDiff), children: content }, i));
210
+ });
211
+ }
@@ -0,0 +1,29 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import cn from "classnames";
3
+ import * as React from "react";
4
+ import { Expand } from "./expand.js";
5
+ /**
6
+ * Custom equality function for React.memo — skips comparing callback/render props.
7
+ */
8
+ function skippedLineIndicatorPropsAreEqual(prev, next) {
9
+ return (prev.num === next.num &&
10
+ prev.blockNumber === next.blockNumber &&
11
+ prev.leftBlockLineNumber === next.leftBlockLineNumber &&
12
+ prev.rightBlockLineNumber === next.rightBlockLineNumber &&
13
+ prev.hideLineNumbers === next.hideLineNumbers &&
14
+ prev.splitView === next.splitView &&
15
+ prev.styles === next.styles);
16
+ }
17
+ /**
18
+ * Memoized component that renders the code fold / skipped line indicator row.
19
+ */
20
+ export const SkippedLineIndicator = React.memo(function SkippedLineIndicator({ num, blockNumber, leftBlockLineNumber, rightBlockLineNumber, hideLineNumbers, splitView, styles, onBlockClick, codeFoldMessageRenderer, renderGutter, }) {
21
+ const handleClick = () => onBlockClick(blockNumber);
22
+ const message = codeFoldMessageRenderer ? (codeFoldMessageRenderer(num, leftBlockLineNumber, rightBlockLineNumber)) : (_jsxs("span", { className: styles.codeFoldContent, children: ["@@ -", leftBlockLineNumber - num, ",", num, " +", rightBlockLineNumber - num, ",", num, " @@"] }));
23
+ const content = (_jsx("td", { className: styles.codeFoldContentContainer, children: _jsx("button", { type: "button", className: styles.codeFoldExpandButton, onClick: handleClick, tabIndex: 0, children: message }) }));
24
+ const isUnifiedViewWithoutLineNumbers = !splitView && !hideLineNumbers;
25
+ const expandGutter = (_jsx("td", { className: styles.codeFoldGutter, children: _jsx(Expand, {}) }));
26
+ return (_jsxs("tr", { className: styles.codeFold, onClick: handleClick, role: "button", tabIndex: 0, children: [!hideLineNumbers && expandGutter, renderGutter ? (_jsx("td", { className: styles.codeFoldGutter })) : null, _jsx("td", { className: cn({
27
+ [styles.codeFoldGutter]: isUnifiedViewWithoutLineNumbers,
28
+ }) }), isUnifiedViewWithoutLineNumbers ? (_jsxs(React.Fragment, { children: [_jsx("td", {}), content] })) : (_jsxs(React.Fragment, { children: [content, renderGutter ? _jsx("td", {}) : null, _jsx("td", {}), _jsx("td", {}), !hideLineNumbers ? _jsx("td", {}) : null] }))] }, `${leftBlockLineNumber}-${rightBlockLineNumber}`));
29
+ }, skippedLineIndicatorPropsAreEqual);
@@ -0,0 +1,431 @@
1
+ import createEmotion from "@emotion/css/create-instance";
2
+ export default (styleOverride, useDarkTheme = false, nonce = "") => {
3
+ const { variables: overrideVariables = {}, ...styles } = styleOverride;
4
+ const themeVariables = {
5
+ light: {
6
+ ...{
7
+ diffViewerBackground: "#fff",
8
+ diffViewerColor: "#212529",
9
+ addedBackground: "#e6ffed",
10
+ addedColor: "#24292e",
11
+ removedBackground: "#ffeef0",
12
+ removedColor: "#24292e",
13
+ changedBackground: "#fffbdd",
14
+ wordAddedBackground: "#acf2bd",
15
+ wordRemovedBackground: "#fdb8c0",
16
+ addedGutterBackground: "#cdffd8",
17
+ removedGutterBackground: "#ffdce0",
18
+ gutterBackground: "#f7f7f7",
19
+ gutterBackgroundDark: "#f3f1f1",
20
+ highlightBackground: "#fffbdd",
21
+ highlightGutterBackground: "#fff5b1",
22
+ codeFoldGutterBackground: "#dbedff",
23
+ codeFoldBackground: "#f1f8ff",
24
+ emptyLineBackground: "#fafbfc",
25
+ gutterColor: "#212529",
26
+ addedGutterColor: "#212529",
27
+ removedGutterColor: "#212529",
28
+ codeFoldContentColor: "#212529",
29
+ diffViewerTitleBackground: "#fafbfc",
30
+ diffViewerTitleColor: "#212529",
31
+ diffViewerTitleBorderColor: "#eee",
32
+ },
33
+ ...(overrideVariables.light || {}),
34
+ },
35
+ dark: {
36
+ ...{
37
+ diffViewerBackground: "#2e303c",
38
+ diffViewerColor: "#FFF",
39
+ addedBackground: "#2ea04326",
40
+ addedColor: "white",
41
+ removedBackground: "#f851491a",
42
+ removedColor: "white",
43
+ changedBackground: "#3e302c",
44
+ wordAddedBackground: "#2ea04366",
45
+ wordRemovedBackground: "#f8514966",
46
+ addedGutterBackground: "#3fb9504d",
47
+ removedGutterBackground: "#f851494d",
48
+ gutterBackground: "#2c2f3a",
49
+ gutterBackgroundDark: "#262933",
50
+ highlightBackground: "#2a3967",
51
+ highlightGutterBackground: "#2d4077",
52
+ codeFoldGutterBackground: "#262831",
53
+ codeFoldBackground: "#262831",
54
+ emptyLineBackground: "#363946",
55
+ gutterColor: "#f0f6fc",
56
+ addedGutterColor: "#f0f6fc",
57
+ removedGutterColor: "#f0f6fc",
58
+ codeFoldContentColor: "#9198a1",
59
+ diffViewerTitleBackground: "#2f323e",
60
+ diffViewerTitleColor: "#f0f6fc",
61
+ diffViewerTitleBorderColor: "#353846",
62
+ },
63
+ ...(overrideVariables.dark || {}),
64
+ },
65
+ };
66
+ const variables = useDarkTheme ? themeVariables.dark : themeVariables.light;
67
+ const { css, cx } = createEmotion({ key: "react-diff", nonce });
68
+ const content = css({
69
+ width: "auto",
70
+ overflow: "hidden",
71
+ label: "content",
72
+ });
73
+ const splitView = css({
74
+ label: "split-view",
75
+ });
76
+ const stickyHeader = css({
77
+ position: "sticky",
78
+ top: 0,
79
+ zIndex: 2,
80
+ label: "sticky-header",
81
+ });
82
+ const summary = css({
83
+ background: variables.diffViewerTitleBackground,
84
+ color: variables.diffViewerTitleColor,
85
+ padding: "0.5em 1em",
86
+ display: "flex",
87
+ alignItems: "center",
88
+ gap: "0.5em",
89
+ fontFamily: "monospace",
90
+ fontSize: 12,
91
+ fill: variables.diffViewerTitleColor,
92
+ });
93
+ const columnHeaders = css({
94
+ display: "flex",
95
+ label: "column-headers",
96
+ });
97
+ const diffContainer = css({
98
+ width: "100%",
99
+ minWidth: "1000px",
100
+ overflowX: "auto",
101
+ tableLayout: "fixed",
102
+ fontSize: 12,
103
+ background: variables.diffViewerBackground,
104
+ pre: {
105
+ margin: 0,
106
+ whiteSpace: "pre-wrap",
107
+ lineHeight: "1.6em",
108
+ width: "fit-content",
109
+ },
110
+ label: "diff-container",
111
+ borderCollapse: "collapse",
112
+ "@media (max-width: 768px)": {
113
+ minWidth: "unset",
114
+ },
115
+ });
116
+ const lineContent = css({
117
+ overflow: "hidden",
118
+ width: "100%",
119
+ });
120
+ const contentText = css({
121
+ color: variables.diffViewerColor,
122
+ whiteSpace: "pre-wrap",
123
+ fontFamily: "monospace",
124
+ lineBreak: "anywhere",
125
+ textDecoration: "none",
126
+ label: "content-text",
127
+ });
128
+ const unselectable = css({
129
+ userSelect: "none",
130
+ label: "unselectable",
131
+ });
132
+ const noWrap = css({
133
+ label: "no-wrap",
134
+ pre: {
135
+ whiteSpace: "pre",
136
+ },
137
+ [`.${contentText}`]: {
138
+ whiteSpace: "pre",
139
+ lineBreak: "auto",
140
+ },
141
+ });
142
+ const allExpandButton = css({
143
+ background: "transparent",
144
+ border: "none",
145
+ cursor: "pointer",
146
+ display: "flex",
147
+ alignItems: "center",
148
+ justifyContent: "center",
149
+ margin: 0,
150
+ label: "all-expand-button",
151
+ ":hover": {
152
+ fill: variables.addedGutterColor,
153
+ },
154
+ ":focus": {
155
+ outline: `1px ${variables.addedGutterColor} solid`,
156
+ },
157
+ });
158
+ const titleBlock = css({
159
+ background: variables.diffViewerTitleBackground,
160
+ padding: "0.5em",
161
+ lineHeight: "1.4em",
162
+ height: "2.4em",
163
+ overflow: "hidden",
164
+ width: "50%",
165
+ borderBottom: `1px solid ${variables.diffViewerTitleBorderColor}`,
166
+ boxSizing: "border-box",
167
+ fontSize: 12,
168
+ label: "title-block",
169
+ ":only-child": {
170
+ width: "100%",
171
+ },
172
+ ":last-child:not(:only-child)": {
173
+ borderLeft: `1px solid ${variables.diffViewerTitleBorderColor}`,
174
+ },
175
+ [`.${contentText}`]: {
176
+ color: variables.diffViewerTitleColor,
177
+ },
178
+ });
179
+ const lineNumber = css({
180
+ color: variables.gutterColor,
181
+ label: "line-number",
182
+ });
183
+ const diffRemoved = css({
184
+ background: variables.removedBackground,
185
+ color: variables.removedColor,
186
+ pre: {
187
+ color: variables.removedColor,
188
+ },
189
+ [`.${lineNumber}`]: {
190
+ color: variables.removedGutterColor,
191
+ },
192
+ label: "diff-removed",
193
+ });
194
+ const diffAdded = css({
195
+ background: variables.addedBackground,
196
+ color: variables.addedColor,
197
+ pre: {
198
+ color: variables.addedColor,
199
+ },
200
+ [`.${lineNumber}`]: {
201
+ color: variables.addedGutterColor,
202
+ },
203
+ label: "diff-added",
204
+ });
205
+ const diffChanged = css({
206
+ background: variables.changedBackground,
207
+ [`.${lineNumber}`]: {
208
+ color: variables.gutterColor,
209
+ },
210
+ label: "diff-changed",
211
+ });
212
+ const wordDiff = css({
213
+ display: "inline",
214
+ textDecoration: "none",
215
+ label: "word-diff",
216
+ });
217
+ const wordAdded = css({
218
+ background: variables.wordAddedBackground,
219
+ label: "word-added",
220
+ });
221
+ const wordRemoved = css({
222
+ background: variables.wordRemovedBackground,
223
+ label: "word-removed",
224
+ });
225
+ const codeFoldGutter = css({
226
+ backgroundColor: variables.codeFoldGutterBackground,
227
+ label: "code-fold-gutter",
228
+ minWidth: "50px",
229
+ width: "50px",
230
+ textAlign: "center",
231
+ fill: variables.codeFoldContentColor,
232
+ });
233
+ const codeFoldContentContainer = css({
234
+ padding: "",
235
+ });
236
+ const codeFoldExpandButton = css({
237
+ background: variables.codeFoldBackground,
238
+ cursor: "pointer",
239
+ display: "inline",
240
+ margin: 0,
241
+ border: "none",
242
+ fill: variables.codeFoldContentColor,
243
+ label: "code-fold-expand-button",
244
+ });
245
+ const codeFoldContent = css({
246
+ color: variables.codeFoldContentColor,
247
+ fontFamily: "monospace",
248
+ label: "code-fold-content",
249
+ });
250
+ const block = css({
251
+ display: "block",
252
+ width: "10px",
253
+ height: "10px",
254
+ backgroundColor: "#ddd",
255
+ borderWidth: "1px",
256
+ borderStyle: "solid",
257
+ borderColor: variables.diffViewerTitleBorderColor,
258
+ });
259
+ const blockAddition = css({
260
+ backgroundColor: variables.wordAddedBackground,
261
+ });
262
+ const blockDeletion = css({
263
+ backgroundColor: variables.wordRemovedBackground,
264
+ });
265
+ const codeFold = css({
266
+ backgroundColor: variables.codeFoldBackground,
267
+ fontSize: 12,
268
+ alignItems: "center",
269
+ userSelect: "none",
270
+ fontWeight: 700,
271
+ cursor: "pointer",
272
+ label: "code-fold",
273
+ "&:hover": {
274
+ color: variables.diffViewerColor,
275
+ fill: variables.diffViewerColor,
276
+ "& *": {
277
+ color: variables.diffViewerColor,
278
+ fill: variables.diffViewerColor,
279
+ },
280
+ },
281
+ a: {
282
+ textDecoration: "underline !important",
283
+ cursor: "pointer",
284
+ pre: {
285
+ display: "inline",
286
+ },
287
+ },
288
+ });
289
+ const emptyLine = css({
290
+ backgroundColor: variables.emptyLineBackground,
291
+ label: "empty-line",
292
+ });
293
+ const commentRow = css({
294
+ label: "comment-row",
295
+ borderTop: `1px solid ${variables.diffViewerTitleBorderColor}`,
296
+ borderBottom: `1px solid ${variables.diffViewerTitleBorderColor}`,
297
+ backgroundColor: variables.diffViewerBackground,
298
+ });
299
+ const commentCell = css({
300
+ label: "comment-cell",
301
+ padding: 0,
302
+ });
303
+ const marker = css({
304
+ width: 28,
305
+ paddingLeft: 10,
306
+ paddingRight: 10,
307
+ userSelect: "none",
308
+ label: "marker",
309
+ [`&.${diffAdded}`]: {
310
+ pre: {
311
+ color: variables.addedColor,
312
+ },
313
+ },
314
+ [`&.${diffRemoved}`]: {
315
+ pre: {
316
+ color: variables.removedColor,
317
+ },
318
+ },
319
+ });
320
+ const highlightedLine = css({
321
+ background: variables.highlightBackground,
322
+ label: "highlighted-line",
323
+ [`.${wordAdded}, .${wordRemoved}`]: {
324
+ backgroundColor: "initial",
325
+ },
326
+ });
327
+ const highlightedGutter = css({
328
+ label: "highlighted-gutter",
329
+ });
330
+ const gutter = css({
331
+ userSelect: "none",
332
+ minWidth: 50,
333
+ width: "50px",
334
+ padding: "0 10px",
335
+ whiteSpace: "nowrap",
336
+ label: "gutter",
337
+ textAlign: "center",
338
+ color: variables.gutterColor,
339
+ background: variables.gutterBackground,
340
+ "&:hover": {
341
+ cursor: "pointer",
342
+ background: variables.gutterBackgroundDark,
343
+ pre: {
344
+ opacity: 1,
345
+ },
346
+ },
347
+ pre: {
348
+ opacity: 0.5,
349
+ textAlign: "center",
350
+ width: "100%",
351
+ },
352
+ [`&.${diffAdded}`]: {
353
+ background: variables.addedGutterBackground,
354
+ },
355
+ [`&.${diffRemoved}`]: {
356
+ background: variables.removedGutterBackground,
357
+ },
358
+ [`&.${highlightedGutter}`]: {
359
+ background: variables.highlightGutterBackground,
360
+ "&:hover": {
361
+ background: variables.highlightGutterBackground,
362
+ },
363
+ },
364
+ });
365
+ const emptyGutter = css({
366
+ "&:hover": {
367
+ background: variables.gutterBackground,
368
+ cursor: "initial",
369
+ },
370
+ label: "empty-gutter",
371
+ });
372
+ const line = css({
373
+ verticalAlign: "baseline",
374
+ label: "line",
375
+ textDecoration: "none",
376
+ });
377
+ const column = css({});
378
+ const defaultStyles = {
379
+ diffContainer,
380
+ diffRemoved,
381
+ diffAdded,
382
+ diffChanged,
383
+ splitView,
384
+ marker,
385
+ highlightedGutter,
386
+ highlightedLine,
387
+ gutter,
388
+ line,
389
+ lineContent,
390
+ wordDiff,
391
+ wordAdded,
392
+ summary,
393
+ block,
394
+ blockAddition,
395
+ blockDeletion,
396
+ wordRemoved,
397
+ noSelect: unselectable,
398
+ noWrap,
399
+ codeFoldGutter,
400
+ codeFoldExpandButton,
401
+ codeFoldContentContainer,
402
+ codeFold,
403
+ emptyGutter,
404
+ emptyLine,
405
+ lineNumber,
406
+ contentText,
407
+ content,
408
+ column,
409
+ codeFoldContent,
410
+ stickyHeader,
411
+ columnHeaders,
412
+ titleBlock,
413
+ allExpandButton,
414
+ commentRow,
415
+ commentCell,
416
+ };
417
+ const computerOverrideStyles = Object.keys(styles).reduce((acc, key) => ({
418
+ ...acc,
419
+ ...{
420
+ [key]: css(styles[key]),
421
+ },
422
+ }), {});
423
+ return Object.keys(defaultStyles).reduce((acc, key) => ({
424
+ ...acc,
425
+ ...{
426
+ [key]: computerOverrideStyles[key]
427
+ ? cx(defaultStyles[key], computerOverrideStyles[key])
428
+ : defaultStyles[key],
429
+ },
430
+ }), {});
431
+ };