@buoy-gg/storage 2.1.14 → 2.1.16
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.
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +345 -173
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +24 -23
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +10 -8
- package/lib/commonjs/storage/stores/storageEventStore.js +13 -3
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +348 -176
- package/lib/module/storage/components/StorageEventDetailContent.js +25 -24
- package/lib/module/storage/components/StorageModalWithTabs.js +10 -8
- package/lib/module/storage/stores/storageEventStore.js +13 -3
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts +6 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -9,185 +9,91 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
var _lineDiff = require("../../../utils/lineDiff");
|
|
10
10
|
var _DiffSummary = require("../components/DiffSummary");
|
|
11
11
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
12
|
+
const DEFAULT_OPTIONS = {
|
|
13
|
+
hideLineNumbers: false,
|
|
14
|
+
disableWordDiff: false,
|
|
15
|
+
showDiffOnly: false,
|
|
16
|
+
compareMethod: "words",
|
|
17
|
+
contextLines: 3,
|
|
18
|
+
lineOffset: 0
|
|
19
|
+
};
|
|
12
20
|
function ThemedSplitView({
|
|
13
21
|
oldValue,
|
|
14
22
|
newValue,
|
|
15
23
|
theme,
|
|
16
|
-
options =
|
|
17
|
-
hideLineNumbers: false,
|
|
18
|
-
disableWordDiff: false,
|
|
19
|
-
showDiffOnly: false,
|
|
20
|
-
compareMethod: "words",
|
|
21
|
-
contextLines: 3,
|
|
22
|
-
lineOffset: 0
|
|
23
|
-
},
|
|
24
|
+
options = DEFAULT_OPTIONS,
|
|
24
25
|
showThemeName = false
|
|
25
26
|
}) {
|
|
26
|
-
|
|
27
|
-
const diffComputeOptions = {
|
|
27
|
+
const dynamicStyles = (0, _react.useMemo)(() => createDynamicStyles(theme), [theme]);
|
|
28
|
+
const diffComputeOptions = (0, _react.useMemo)(() => ({
|
|
28
29
|
compareMethod: options.compareMethod,
|
|
29
30
|
disableWordDiff: options.disableWordDiff,
|
|
30
31
|
showDiffOnly: options.showDiffOnly,
|
|
31
32
|
contextLines: options.contextLines
|
|
32
|
-
};
|
|
33
|
-
const lineDiffs = (0, _lineDiff.computeLineDiff)(oldValue, newValue, diffComputeOptions);
|
|
33
|
+
}), [options.compareMethod, options.disableWordDiff, options.showDiffOnly, options.contextLines]);
|
|
34
|
+
const lineDiffs = (0, _react.useMemo)(() => (0, _lineDiff.computeLineDiff)(oldValue, newValue, diffComputeOptions), [oldValue, newValue, diffComputeOptions]);
|
|
34
35
|
|
|
35
|
-
//
|
|
36
|
-
const
|
|
36
|
+
// One pass for the summary instead of three .filter() calls per render.
|
|
37
|
+
const summary = (0, _react.useMemo)(() => {
|
|
38
|
+
let added = 0;
|
|
39
|
+
let removed = 0;
|
|
40
|
+
let modified = 0;
|
|
41
|
+
for (const d of lineDiffs) {
|
|
42
|
+
if (d.type === _lineDiff.DiffType.ADDED) added++;else if (d.type === _lineDiff.DiffType.REMOVED) removed++;else if (d.type === _lineDiff.DiffType.MODIFIED) modified++;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
added,
|
|
46
|
+
removed,
|
|
47
|
+
modified
|
|
48
|
+
};
|
|
49
|
+
}, [lineDiffs]);
|
|
37
50
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (word.type === _lineDiff.DiffType.ADDED) {
|
|
43
|
-
backgroundColor = theme.addedWordHighlight;
|
|
44
|
-
} else if (word.type === _lineDiff.DiffType.REMOVED) {
|
|
45
|
-
backgroundColor = theme.removedWordHighlight;
|
|
46
|
-
}
|
|
47
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
48
|
-
style: [dynamicStyles.wordDiff, {
|
|
49
|
-
backgroundColor
|
|
50
|
-
}],
|
|
51
|
-
children: word.value
|
|
52
|
-
}, idx);
|
|
53
|
-
});
|
|
54
|
-
};
|
|
51
|
+
// Auto-collapse runs of unchanged lines that aren't near a change. Keeps
|
|
52
|
+
// CONTEXT_LINES on either side of every change visible (GitHub-style), folds
|
|
53
|
+
// the rest into a tappable header.
|
|
54
|
+
const [expandedFolds, setExpandedFolds] = (0, _react.useState)(() => new Set());
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
// Reset fold expansion when the user actually switches comparisons. Parent
|
|
57
|
+
// memoizes oldValue/newValue, so this only fires on a real change — not on
|
|
58
|
+
// every re-render. First run is a no-op (initial state is already empty).
|
|
59
|
+
const hasMountedRef = (0, _react.useRef)(false);
|
|
60
|
+
(0, _react.useEffect)(() => {
|
|
61
|
+
if (!hasMountedRef.current) {
|
|
62
|
+
hasMountedRef.current = true;
|
|
63
|
+
return;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
text: theme.unchangedText,
|
|
88
|
-
markerBg: "transparent"
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// Render a single line side (left or right)
|
|
94
|
-
const renderLineSide = (lineNumber, content, type, marker, isEmpty = false) => {
|
|
95
|
-
if (isEmpty) {
|
|
96
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
97
|
-
children: [!options.hideLineNumbers && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
98
|
-
style: [dynamicStyles.gutter, dynamicStyles.emptyGutter],
|
|
99
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
100
|
-
style: dynamicStyles.lineNumber,
|
|
101
|
-
children: " "
|
|
102
|
-
})
|
|
103
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
104
|
-
style: [dynamicStyles.marker, dynamicStyles.emptyMarker],
|
|
105
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
106
|
-
style: dynamicStyles.markerText,
|
|
107
|
-
children: " "
|
|
108
|
-
})
|
|
109
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
110
|
-
style: [dynamicStyles.contentCell, dynamicStyles.emptyContent],
|
|
111
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
112
|
-
style: dynamicStyles.content,
|
|
113
|
-
children: " "
|
|
114
|
-
})
|
|
115
|
-
})]
|
|
65
|
+
setExpandedFolds(new Set());
|
|
66
|
+
}, [oldValue, newValue]);
|
|
67
|
+
const items = (0, _react.useMemo)(() => buildSplitItems(lineDiffs, expandedFolds), [lineDiffs, expandedFolds]);
|
|
68
|
+
const toggleFold = (0, _react.useCallback)(foldId => {
|
|
69
|
+
setExpandedFolds(prev => {
|
|
70
|
+
const next = new Set(prev);
|
|
71
|
+
if (next.has(foldId)) next.delete(foldId);else next.add(foldId);
|
|
72
|
+
return next;
|
|
73
|
+
});
|
|
74
|
+
}, []);
|
|
75
|
+
const keyExtractor = (0, _react.useCallback)(item => item.kind === "row" ? `r:${item.diffIndex}` : `f:${item.foldId}`, []);
|
|
76
|
+
const renderItem = (0, _react.useCallback)(({
|
|
77
|
+
item
|
|
78
|
+
}) => {
|
|
79
|
+
if (item.kind === "fold") {
|
|
80
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(FoldHeader, {
|
|
81
|
+
foldId: item.foldId,
|
|
82
|
+
count: item.count,
|
|
83
|
+
expanded: item.expanded,
|
|
84
|
+
theme: theme,
|
|
85
|
+
styles: dynamicStyles,
|
|
86
|
+
onToggle: toggleFold
|
|
116
87
|
});
|
|
117
88
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
125
|
-
style: dynamicStyles.lineNumber,
|
|
126
|
-
children: lineNumber || " "
|
|
127
|
-
})
|
|
128
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
129
|
-
style: [dynamicStyles.marker, {
|
|
130
|
-
backgroundColor: colors.markerBg
|
|
131
|
-
}],
|
|
132
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
133
|
-
style: [dynamicStyles.markerText, {
|
|
134
|
-
color: theme.markerText
|
|
135
|
-
}],
|
|
136
|
-
children: marker
|
|
137
|
-
})
|
|
138
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
139
|
-
style: [dynamicStyles.contentCell, {
|
|
140
|
-
backgroundColor: colors.background
|
|
141
|
-
}],
|
|
142
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
143
|
-
style: [dynamicStyles.content, {
|
|
144
|
-
color: colors.text
|
|
145
|
-
}],
|
|
146
|
-
children: Array.isArray(content) ? renderWordDiff(content) : content || " "
|
|
147
|
-
})
|
|
148
|
-
})]
|
|
89
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(DiffRow, {
|
|
90
|
+
diff: item.diff,
|
|
91
|
+
showSeparator: false,
|
|
92
|
+
theme: theme,
|
|
93
|
+
options: options,
|
|
94
|
+
styles: dynamicStyles
|
|
149
95
|
});
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// Check if we should show a separator (gap in line numbers)
|
|
153
|
-
const shouldShowSeparator = (idx, diffs) => {
|
|
154
|
-
if (!options.showDiffOnly || idx === 0) return false;
|
|
155
|
-
const prevDiff = diffs[idx - 1];
|
|
156
|
-
const currDiff = diffs[idx];
|
|
157
|
-
|
|
158
|
-
// Check for gap in line numbers
|
|
159
|
-
const leftGap = currDiff.leftLineNumber && prevDiff.leftLineNumber && currDiff.leftLineNumber - prevDiff.leftLineNumber > 1;
|
|
160
|
-
const rightGap = currDiff.rightLineNumber && prevDiff.rightLineNumber && currDiff.rightLineNumber - prevDiff.rightLineNumber > 1;
|
|
161
|
-
return leftGap || rightGap;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// Render a complete row with both left and right sides
|
|
165
|
-
const renderDiffRow = (diff, idx, diffs) => {
|
|
166
|
-
const isRemoved = diff.type === _lineDiff.DiffType.REMOVED;
|
|
167
|
-
const isAdded = diff.type === _lineDiff.DiffType.ADDED;
|
|
168
|
-
const isModified = diff.type === _lineDiff.DiffType.MODIFIED;
|
|
169
|
-
const isDefault = diff.type === _lineDiff.DiffType.DEFAULT;
|
|
170
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
|
|
171
|
-
children: [shouldShowSeparator(idx, diffs) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
172
|
-
style: dynamicStyles.separator,
|
|
173
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
174
|
-
style: dynamicStyles.separatorText,
|
|
175
|
-
children: "\u2022 \u2022 \u2022"
|
|
176
|
-
})
|
|
177
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
178
|
-
style: dynamicStyles.row,
|
|
179
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
180
|
-
style: dynamicStyles.leftSide,
|
|
181
|
-
children: isRemoved || isModified || isDefault ? renderLineSide(diff.leftLineNumber, diff.leftContent, isModified ? _lineDiff.DiffType.REMOVED : diff.type, isRemoved || isModified ? "-" : " ") : renderLineSide(undefined, undefined, _lineDiff.DiffType.DEFAULT, " ", true)
|
|
182
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
183
|
-
style: dynamicStyles.centerDivider
|
|
184
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
185
|
-
style: dynamicStyles.rightSide,
|
|
186
|
-
children: isAdded || isModified || isDefault ? renderLineSide(diff.rightLineNumber, diff.rightContent, isModified ? _lineDiff.DiffType.ADDED : diff.type, isAdded || isModified ? "+" : " ") : renderLineSide(undefined, undefined, _lineDiff.DiffType.DEFAULT, " ", true)
|
|
187
|
-
})]
|
|
188
|
-
})]
|
|
189
|
-
}, idx);
|
|
190
|
-
};
|
|
96
|
+
}, [theme, options, dynamicStyles, toggleFold]);
|
|
191
97
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
192
98
|
style: [dynamicStyles.container, theme.glowColor && {
|
|
193
99
|
shadowColor: theme.glowColor,
|
|
@@ -225,24 +131,276 @@ function ThemedSplitView({
|
|
|
225
131
|
})
|
|
226
132
|
})]
|
|
227
133
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_DiffSummary.DiffSummary, {
|
|
228
|
-
added:
|
|
229
|
-
removed:
|
|
230
|
-
modified:
|
|
134
|
+
added: summary.added,
|
|
135
|
+
removed: summary.removed,
|
|
136
|
+
modified: summary.modified,
|
|
231
137
|
theme: theme
|
|
232
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.
|
|
138
|
+
}), lineDiffs.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
139
|
+
style: dynamicStyles.emptyState,
|
|
140
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
141
|
+
style: dynamicStyles.emptyText,
|
|
142
|
+
children: options.showDiffOnly ? "No differences found" : "No content to display"
|
|
143
|
+
})
|
|
144
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
|
|
233
145
|
style: dynamicStyles.scrollView,
|
|
234
|
-
showsVerticalScrollIndicator: false,
|
|
235
146
|
contentContainerStyle: dynamicStyles.scrollContent,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
147
|
+
data: items,
|
|
148
|
+
renderItem: renderItem,
|
|
149
|
+
keyExtractor: keyExtractor,
|
|
150
|
+
showsVerticalScrollIndicator: false,
|
|
151
|
+
initialNumToRender: 20,
|
|
152
|
+
maxToRenderPerBatch: 20,
|
|
153
|
+
windowSize: 7,
|
|
154
|
+
removeClippedSubviews: true
|
|
155
|
+
})]
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Number of context lines kept visible on either side of every change.
|
|
160
|
+
const CONTEXT_LINES = 3;
|
|
161
|
+
// Don't bother folding tiny gaps — toggling them is more friction than scroll.
|
|
162
|
+
const MIN_FOLD_SIZE = 4;
|
|
163
|
+
function buildSplitItems(lineDiffs, expandedFolds) {
|
|
164
|
+
const n = lineDiffs.length;
|
|
165
|
+
if (n === 0) return [];
|
|
166
|
+
|
|
167
|
+
// Mark every line that should always be visible: any change, plus
|
|
168
|
+
// CONTEXT_LINES on either side of it.
|
|
169
|
+
const visible = new Array(n).fill(false);
|
|
170
|
+
for (let i = 0; i < n; i++) {
|
|
171
|
+
if (lineDiffs[i].type !== _lineDiff.DiffType.DEFAULT) {
|
|
172
|
+
const start = Math.max(0, i - CONTEXT_LINES);
|
|
173
|
+
const end = Math.min(n - 1, i + CONTEXT_LINES);
|
|
174
|
+
for (let k = start; k <= end; k++) visible[k] = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const items = [];
|
|
178
|
+
let foldId = 0;
|
|
179
|
+
let i = 0;
|
|
180
|
+
while (i < n) {
|
|
181
|
+
if (visible[i]) {
|
|
182
|
+
items.push({
|
|
183
|
+
kind: "row",
|
|
184
|
+
diff: lineDiffs[i],
|
|
185
|
+
diffIndex: i
|
|
186
|
+
});
|
|
187
|
+
i++;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
let j = i;
|
|
191
|
+
while (j < n && !visible[j]) j++;
|
|
192
|
+
const runLength = j - i;
|
|
193
|
+
if (runLength < MIN_FOLD_SIZE) {
|
|
194
|
+
for (let k = i; k < j; k++) {
|
|
195
|
+
items.push({
|
|
196
|
+
kind: "row",
|
|
197
|
+
diff: lineDiffs[k],
|
|
198
|
+
diffIndex: k
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
const id = foldId++;
|
|
203
|
+
const expanded = expandedFolds.has(id);
|
|
204
|
+
items.push({
|
|
205
|
+
kind: "fold",
|
|
206
|
+
foldId: id,
|
|
207
|
+
count: runLength,
|
|
208
|
+
expanded
|
|
209
|
+
});
|
|
210
|
+
if (expanded) {
|
|
211
|
+
for (let k = i; k < j; k++) {
|
|
212
|
+
items.push({
|
|
213
|
+
kind: "row",
|
|
214
|
+
diff: lineDiffs[k],
|
|
215
|
+
diffIndex: k
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
i = j;
|
|
221
|
+
}
|
|
222
|
+
return items;
|
|
223
|
+
}
|
|
224
|
+
const FoldHeader = /*#__PURE__*/(0, _react.memo)(function FoldHeader({
|
|
225
|
+
foldId,
|
|
226
|
+
count,
|
|
227
|
+
expanded,
|
|
228
|
+
theme,
|
|
229
|
+
styles,
|
|
230
|
+
onToggle
|
|
231
|
+
}) {
|
|
232
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
233
|
+
style: [styles.foldHeader, {
|
|
234
|
+
backgroundColor: theme.separatorBackground
|
|
235
|
+
}],
|
|
236
|
+
onPress: () => onToggle(foldId),
|
|
237
|
+
activeOpacity: 0.7,
|
|
238
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
239
|
+
style: [styles.foldHeaderText, {
|
|
240
|
+
color: theme.separatorText
|
|
241
|
+
}],
|
|
242
|
+
children: expanded ? `▼ ${count} unchanged line${count === 1 ? "" : "s"} — tap to hide` : `▶ ${count} unchanged line${count === 1 ? "" : "s"} — tap to expand`
|
|
243
|
+
})
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
const DiffRow = /*#__PURE__*/(0, _react.memo)(function DiffRow({
|
|
247
|
+
diff,
|
|
248
|
+
showSeparator,
|
|
249
|
+
theme,
|
|
250
|
+
options,
|
|
251
|
+
styles
|
|
252
|
+
}) {
|
|
253
|
+
const isRemoved = diff.type === _lineDiff.DiffType.REMOVED;
|
|
254
|
+
const isAdded = diff.type === _lineDiff.DiffType.ADDED;
|
|
255
|
+
const isModified = diff.type === _lineDiff.DiffType.MODIFIED;
|
|
256
|
+
const isDefault = diff.type === _lineDiff.DiffType.DEFAULT;
|
|
257
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
|
|
258
|
+
children: [showSeparator && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
259
|
+
style: styles.separator,
|
|
260
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
261
|
+
style: styles.separatorText,
|
|
262
|
+
children: "\u2022 \u2022 \u2022"
|
|
263
|
+
})
|
|
264
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
265
|
+
style: styles.row,
|
|
266
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
267
|
+
style: styles.leftSide,
|
|
268
|
+
children: isRemoved || isModified || isDefault ? /*#__PURE__*/(0, _jsxRuntime.jsx)(LineSide, {
|
|
269
|
+
lineNumber: diff.leftLineNumber,
|
|
270
|
+
content: diff.leftContent,
|
|
271
|
+
type: isModified ? _lineDiff.DiffType.REMOVED : diff.type,
|
|
272
|
+
marker: isRemoved || isModified ? "-" : " ",
|
|
273
|
+
theme: theme,
|
|
274
|
+
options: options,
|
|
275
|
+
styles: styles
|
|
276
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(EmptyLineSide, {
|
|
277
|
+
options: options,
|
|
278
|
+
styles: styles
|
|
279
|
+
})
|
|
280
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
281
|
+
style: styles.centerDivider
|
|
282
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
283
|
+
style: styles.rightSide,
|
|
284
|
+
children: isAdded || isModified || isDefault ? /*#__PURE__*/(0, _jsxRuntime.jsx)(LineSide, {
|
|
285
|
+
lineNumber: diff.rightLineNumber,
|
|
286
|
+
content: diff.rightContent,
|
|
287
|
+
type: isModified ? _lineDiff.DiffType.ADDED : diff.type,
|
|
288
|
+
marker: isAdded || isModified ? "+" : " ",
|
|
289
|
+
theme: theme,
|
|
290
|
+
options: options,
|
|
291
|
+
styles: styles
|
|
292
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(EmptyLineSide, {
|
|
293
|
+
options: options,
|
|
294
|
+
styles: styles
|
|
241
295
|
})
|
|
242
|
-
})
|
|
296
|
+
})]
|
|
297
|
+
})]
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
function LineSide({
|
|
301
|
+
lineNumber,
|
|
302
|
+
content,
|
|
303
|
+
type,
|
|
304
|
+
marker,
|
|
305
|
+
theme,
|
|
306
|
+
options,
|
|
307
|
+
styles
|
|
308
|
+
}) {
|
|
309
|
+
const colors = getDiffColors(type, theme);
|
|
310
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
|
|
311
|
+
children: [!options.hideLineNumbers && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
312
|
+
style: [styles.gutter, {
|
|
313
|
+
backgroundColor: theme.lineNumberBackground
|
|
314
|
+
}],
|
|
315
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
316
|
+
style: styles.lineNumber,
|
|
317
|
+
children: lineNumber || " "
|
|
318
|
+
})
|
|
319
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
320
|
+
style: [styles.marker, {
|
|
321
|
+
backgroundColor: colors.markerBg
|
|
322
|
+
}],
|
|
323
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
324
|
+
style: [styles.markerText, {
|
|
325
|
+
color: theme.markerText
|
|
326
|
+
}],
|
|
327
|
+
children: marker
|
|
328
|
+
})
|
|
329
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
330
|
+
style: [styles.contentCell, {
|
|
331
|
+
backgroundColor: colors.background
|
|
332
|
+
}],
|
|
333
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
334
|
+
style: [styles.content, {
|
|
335
|
+
color: colors.text
|
|
336
|
+
}],
|
|
337
|
+
children: Array.isArray(content) ? content.map((word, idx) => {
|
|
338
|
+
let backgroundColor = "transparent";
|
|
339
|
+
if (word.type === _lineDiff.DiffType.ADDED) {
|
|
340
|
+
backgroundColor = theme.addedWordHighlight;
|
|
341
|
+
} else if (word.type === _lineDiff.DiffType.REMOVED) {
|
|
342
|
+
backgroundColor = theme.removedWordHighlight;
|
|
343
|
+
}
|
|
344
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
345
|
+
style: [styles.wordDiff, {
|
|
346
|
+
backgroundColor
|
|
347
|
+
}],
|
|
348
|
+
children: word.value
|
|
349
|
+
}, idx);
|
|
350
|
+
}) : content || " "
|
|
351
|
+
})
|
|
243
352
|
})]
|
|
244
353
|
});
|
|
245
354
|
}
|
|
355
|
+
function EmptyLineSide({
|
|
356
|
+
options,
|
|
357
|
+
styles
|
|
358
|
+
}) {
|
|
359
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.Fragment, {
|
|
360
|
+
children: [!options.hideLineNumbers && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
361
|
+
style: [styles.gutter, styles.emptyGutter],
|
|
362
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
363
|
+
style: styles.lineNumber,
|
|
364
|
+
children: " "
|
|
365
|
+
})
|
|
366
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
367
|
+
style: [styles.marker, styles.emptyMarker],
|
|
368
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
369
|
+
style: styles.markerText,
|
|
370
|
+
children: " "
|
|
371
|
+
})
|
|
372
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
373
|
+
style: [styles.contentCell, styles.emptyContent],
|
|
374
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
375
|
+
style: styles.content,
|
|
376
|
+
children: " "
|
|
377
|
+
})
|
|
378
|
+
})]
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function getDiffColors(type, theme) {
|
|
382
|
+
switch (type) {
|
|
383
|
+
case _lineDiff.DiffType.ADDED:
|
|
384
|
+
return {
|
|
385
|
+
background: theme.addedBackground,
|
|
386
|
+
text: theme.addedText,
|
|
387
|
+
markerBg: theme.markerAddedBackground
|
|
388
|
+
};
|
|
389
|
+
case _lineDiff.DiffType.REMOVED:
|
|
390
|
+
return {
|
|
391
|
+
background: theme.removedBackground,
|
|
392
|
+
text: theme.removedText,
|
|
393
|
+
markerBg: theme.markerRemovedBackground
|
|
394
|
+
};
|
|
395
|
+
case _lineDiff.DiffType.DEFAULT:
|
|
396
|
+
default:
|
|
397
|
+
return {
|
|
398
|
+
background: theme.unchangedBackground,
|
|
399
|
+
text: theme.unchangedText,
|
|
400
|
+
markerBg: "transparent"
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}
|
|
246
404
|
|
|
247
405
|
// Create dynamic styles based on theme
|
|
248
406
|
function createDynamicStyles(theme) {
|
|
@@ -404,6 +562,20 @@ function createDynamicStyles(theme) {
|
|
|
404
562
|
fontFamily: "monospace",
|
|
405
563
|
letterSpacing: 2
|
|
406
564
|
},
|
|
565
|
+
foldHeader: {
|
|
566
|
+
paddingVertical: 6,
|
|
567
|
+
paddingHorizontal: 12,
|
|
568
|
+
alignItems: "center",
|
|
569
|
+
justifyContent: "center",
|
|
570
|
+
borderTopWidth: 1,
|
|
571
|
+
borderBottomWidth: 1,
|
|
572
|
+
borderColor: theme.borderColor
|
|
573
|
+
},
|
|
574
|
+
foldHeaderText: {
|
|
575
|
+
fontSize: 10,
|
|
576
|
+
fontFamily: "monospace",
|
|
577
|
+
letterSpacing: 0.5
|
|
578
|
+
},
|
|
407
579
|
emptyState: {
|
|
408
580
|
padding: 40,
|
|
409
581
|
alignItems: "center",
|
|
@@ -67,6 +67,15 @@ function formatTimeWithMs(date) {
|
|
|
67
67
|
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
68
68
|
return `${h}:${m}:${s}.${ms}`;
|
|
69
69
|
}
|
|
70
|
+
const SPLIT_VIEW_OPTIONS = {
|
|
71
|
+
hideLineNumbers: false,
|
|
72
|
+
disableWordDiff: false,
|
|
73
|
+
showDiffOnly: false,
|
|
74
|
+
compareMethod: "words",
|
|
75
|
+
contextLines: 3,
|
|
76
|
+
lineOffset: 0
|
|
77
|
+
};
|
|
78
|
+
const SPLIT_VIEW_DIFFERENCES = [];
|
|
70
79
|
function StorageEventDetailContent({
|
|
71
80
|
conversation,
|
|
72
81
|
selectedEventIndex = 0,
|
|
@@ -263,38 +272,30 @@ function StorageEventDetailContent({
|
|
|
263
272
|
});
|
|
264
273
|
}, [valueToShow, action, actionColor, valueType]);
|
|
265
274
|
|
|
275
|
+
// Memoized so the diff viewers don't see new oldValue/newValue refs on every
|
|
276
|
+
// re-render. parseValue runs JSON.parse, which would otherwise produce fresh
|
|
277
|
+
// object identities every time and force expensive recomputations downstream
|
|
278
|
+
// (line diff, tree diff, expansion-state effects, etc.).
|
|
279
|
+
const previousValue = (0, _react.useMemo)(() => (0, _sharedUi.parseValue)(leftEvent?.data?.value ?? null), [leftEvent]);
|
|
280
|
+
const currentValue = (0, _react.useMemo)(() => (0, _sharedUi.parseValue)(rightEvent?.data?.value), [rightEvent]);
|
|
281
|
+
|
|
266
282
|
// Render diff content
|
|
267
283
|
const renderDiffContent = (0, _react.useCallback)(() => {
|
|
268
|
-
const previousValue = (0, _sharedUi.parseValue)(leftEvent?.data?.value ?? null);
|
|
269
|
-
const currentValue = (0, _sharedUi.parseValue)(rightEvent?.data?.value);
|
|
270
284
|
if (diffMode === "split") {
|
|
271
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
newValue: currentValue,
|
|
279
|
-
differences: [],
|
|
280
|
-
theme: _diffThemes.diffThemes.devToolsDefault,
|
|
281
|
-
options: {
|
|
282
|
-
hideLineNumbers: false,
|
|
283
|
-
disableWordDiff: false,
|
|
284
|
-
showDiffOnly: false,
|
|
285
|
-
compareMethod: "words",
|
|
286
|
-
contextLines: 3,
|
|
287
|
-
lineOffset: 0
|
|
288
|
-
},
|
|
289
|
-
showThemeName: false
|
|
290
|
-
})
|
|
285
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ThemedSplitView.ThemedSplitView, {
|
|
286
|
+
oldValue: previousValue,
|
|
287
|
+
newValue: currentValue,
|
|
288
|
+
differences: SPLIT_VIEW_DIFFERENCES,
|
|
289
|
+
theme: _diffThemes.diffThemes.devToolsDefault,
|
|
290
|
+
options: SPLIT_VIEW_OPTIONS,
|
|
291
|
+
showThemeName: false
|
|
291
292
|
});
|
|
292
293
|
}
|
|
293
294
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeDiffViewer.TreeDiffViewer, {
|
|
294
295
|
oldValue: previousValue,
|
|
295
296
|
newValue: currentValue
|
|
296
297
|
});
|
|
297
|
-
}, [
|
|
298
|
+
}, [previousValue, currentValue, diffMode]);
|
|
298
299
|
|
|
299
300
|
// Footer navigation handlers
|
|
300
301
|
const handleFooterPrevious = (0, _react.useCallback)(() => {
|