@buoy-gg/shared-ui 7.0.16 → 7.0.18

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 (111) hide show
  1. package/lib/commonjs/dataViewer/ThemedSplitView.js +622 -0
  2. package/lib/commonjs/dataViewer/index.js +32 -0
  3. package/lib/commonjs/dataViewer/objectDiff.js +304 -0
  4. package/lib/commonjs/hooks/index.js +7 -0
  5. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  6. package/lib/commonjs/hooks/usePersistedState.js +71 -0
  7. package/lib/commonjs/icons/index.js +14 -0
  8. package/lib/commonjs/index.js +61 -0
  9. package/lib/commonjs/storage/devToolsStorageKeys.js +31 -0
  10. package/lib/commonjs/sync/wireBudget.js +97 -0
  11. package/lib/commonjs/ui/components/HeaderSearchButton.js +6 -1
  12. package/lib/commonjs/ui/components/ModalHeader.js +69 -0
  13. package/lib/commonjs/ui/components/ToolButton.js +90 -0
  14. package/lib/commonjs/ui/components/index.js +7 -0
  15. package/lib/commonjs/ui/index.js +8 -0
  16. package/lib/commonjs/ui/toolTheme.js +96 -0
  17. package/lib/commonjs/utils/index.js +38 -0
  18. package/lib/commonjs/utils/persistentStorage.js +6 -3
  19. package/lib/commonjs/utils/stateSnapshotRegistry.js +71 -0
  20. package/lib/module/dataViewer/ThemedSplitView.js +617 -0
  21. package/lib/module/dataViewer/index.js +7 -1
  22. package/lib/module/dataViewer/objectDiff.js +297 -0
  23. package/lib/module/hooks/index.js +1 -0
  24. package/lib/module/hooks/safe-area-impl.js +1 -1
  25. package/lib/module/hooks/usePersistedState.js +68 -0
  26. package/lib/module/icons/index.js +1 -0
  27. package/lib/module/index.js +6 -1
  28. package/lib/module/storage/devToolsStorageKeys.js +31 -0
  29. package/lib/module/sync/wireBudget.js +92 -0
  30. package/lib/module/ui/components/HeaderSearchButton.js +6 -1
  31. package/lib/module/ui/components/ModalHeader.js +70 -2
  32. package/lib/module/ui/components/ToolButton.js +86 -0
  33. package/lib/module/ui/components/index.js +1 -0
  34. package/lib/module/ui/index.js +3 -0
  35. package/lib/module/ui/toolTheme.js +93 -0
  36. package/lib/module/utils/index.js +2 -0
  37. package/lib/module/utils/persistentStorage.js +6 -3
  38. package/lib/module/utils/stateSnapshotRegistry.js +64 -0
  39. package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts +28 -0
  40. package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -0
  41. package/lib/typescript/commonjs/dataViewer/index.d.ts +2 -0
  42. package/lib/typescript/commonjs/dataViewer/index.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/dataViewer/objectDiff.d.ts +67 -0
  44. package/lib/typescript/commonjs/dataViewer/objectDiff.d.ts.map +1 -0
  45. package/lib/typescript/commonjs/hooks/index.d.ts +1 -0
  46. package/lib/typescript/commonjs/hooks/index.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  48. package/lib/typescript/commonjs/hooks/usePersistedState.d.ts +16 -0
  49. package/lib/typescript/commonjs/hooks/usePersistedState.d.ts.map +1 -0
  50. package/lib/typescript/commonjs/icons/index.d.ts +1 -0
  51. package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/index.d.ts +3 -2
  53. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  54. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +28 -0
  55. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/sync/wireBudget.d.ts +50 -0
  57. package/lib/typescript/commonjs/sync/wireBudget.d.ts.map +1 -0
  58. package/lib/typescript/commonjs/ui/components/HeaderSearchButton.d.ts +3 -1
  59. package/lib/typescript/commonjs/ui/components/HeaderSearchButton.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts +13 -1
  61. package/lib/typescript/commonjs/ui/components/ModalHeader.d.ts.map +1 -1
  62. package/lib/typescript/commonjs/ui/components/ToolButton.d.ts +24 -0
  63. package/lib/typescript/commonjs/ui/components/ToolButton.d.ts.map +1 -0
  64. package/lib/typescript/commonjs/ui/components/index.d.ts +4 -0
  65. package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
  66. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  67. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  68. package/lib/typescript/commonjs/ui/toolTheme.d.ts +68 -0
  69. package/lib/typescript/commonjs/ui/toolTheme.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/utils/index.d.ts +2 -0
  71. package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/utils/persistentStorage.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/utils/stateSnapshotRegistry.d.ts +111 -0
  74. package/lib/typescript/commonjs/utils/stateSnapshotRegistry.d.ts.map +1 -0
  75. package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts +28 -0
  76. package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -0
  77. package/lib/typescript/module/dataViewer/index.d.ts +2 -0
  78. package/lib/typescript/module/dataViewer/index.d.ts.map +1 -1
  79. package/lib/typescript/module/dataViewer/objectDiff.d.ts +67 -0
  80. package/lib/typescript/module/dataViewer/objectDiff.d.ts.map +1 -0
  81. package/lib/typescript/module/hooks/index.d.ts +1 -0
  82. package/lib/typescript/module/hooks/index.d.ts.map +1 -1
  83. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  84. package/lib/typescript/module/hooks/usePersistedState.d.ts +16 -0
  85. package/lib/typescript/module/hooks/usePersistedState.d.ts.map +1 -0
  86. package/lib/typescript/module/icons/index.d.ts +1 -0
  87. package/lib/typescript/module/icons/index.d.ts.map +1 -1
  88. package/lib/typescript/module/index.d.ts +3 -2
  89. package/lib/typescript/module/index.d.ts.map +1 -1
  90. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +28 -0
  91. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  92. package/lib/typescript/module/sync/wireBudget.d.ts +50 -0
  93. package/lib/typescript/module/sync/wireBudget.d.ts.map +1 -0
  94. package/lib/typescript/module/ui/components/HeaderSearchButton.d.ts +3 -1
  95. package/lib/typescript/module/ui/components/HeaderSearchButton.d.ts.map +1 -1
  96. package/lib/typescript/module/ui/components/ModalHeader.d.ts +13 -1
  97. package/lib/typescript/module/ui/components/ModalHeader.d.ts.map +1 -1
  98. package/lib/typescript/module/ui/components/ToolButton.d.ts +24 -0
  99. package/lib/typescript/module/ui/components/ToolButton.d.ts.map +1 -0
  100. package/lib/typescript/module/ui/components/index.d.ts +4 -0
  101. package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
  102. package/lib/typescript/module/ui/index.d.ts +1 -0
  103. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  104. package/lib/typescript/module/ui/toolTheme.d.ts +68 -0
  105. package/lib/typescript/module/ui/toolTheme.d.ts.map +1 -0
  106. package/lib/typescript/module/utils/index.d.ts +2 -0
  107. package/lib/typescript/module/utils/index.d.ts.map +1 -1
  108. package/lib/typescript/module/utils/persistentStorage.d.ts.map +1 -1
  109. package/lib/typescript/module/utils/stateSnapshotRegistry.d.ts +111 -0
  110. package/lib/typescript/module/utils/stateSnapshotRegistry.d.ts.map +1 -0
  111. package/package.json +4 -4
@@ -0,0 +1,617 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ThemedSplitView — the production-grade split diff renderer, promoted from
5
+ * packages/storage (its original home) so every tool shares ONE copy.
6
+ *
7
+ * Compared to SplitDiffViewer: GitHub-style auto-folding of unchanged runs
8
+ * (CONTEXT_LINES / MIN_FOLD_SIZE), a memoized single-pass summary, FlatList
9
+ * virtualization when scrolling — and a documented inline branch, because a
10
+ * FlatList can't virtualize inside a page ScrollView (no viewport of its own
11
+ * to measure against), so inline mode renders rows directly.
12
+ */
13
+ import { Fragment, memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
14
+ import { View, Text, FlatList, StyleSheet, TouchableOpacity } from "react-native";
15
+ import { computeLineDiff, DiffType } from "./lineDiff.js";
16
+ import { DiffSummary } from "./DiffSummary.js";
17
+
18
+ /** Rendering options (the shape storage's DiffOptionsPanel edits). */
19
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
+ const DEFAULT_OPTIONS = {
21
+ hideLineNumbers: false,
22
+ disableWordDiff: false,
23
+ showDiffOnly: false,
24
+ compareMethod: "words",
25
+ contextLines: 3,
26
+ lineOffset: 0
27
+ };
28
+ export function ThemedSplitView({
29
+ oldValue,
30
+ newValue,
31
+ theme,
32
+ options = DEFAULT_OPTIONS,
33
+ showThemeName = false,
34
+ scrollEnabled = true
35
+ }) {
36
+ // Inline mode drops the fixed 400pt pane so the container grows with the
37
+ // rows; the ancestor ScrollView is what scrolls them.
38
+ const dynamicStyles = useMemo(() => createDynamicStyles(theme, scrollEnabled), [theme, scrollEnabled]);
39
+ const diffComputeOptions = useMemo(() => ({
40
+ compareMethod: options.compareMethod,
41
+ disableWordDiff: options.disableWordDiff,
42
+ showDiffOnly: options.showDiffOnly,
43
+ contextLines: options.contextLines
44
+ }), [options.compareMethod, options.disableWordDiff, options.showDiffOnly, options.contextLines]);
45
+ const lineDiffs = useMemo(() => computeLineDiff(oldValue, newValue, diffComputeOptions), [oldValue, newValue, diffComputeOptions]);
46
+
47
+ // One pass for the summary instead of three .filter() calls per render.
48
+ const summary = useMemo(() => {
49
+ let added = 0;
50
+ let removed = 0;
51
+ let modified = 0;
52
+ for (const d of lineDiffs) {
53
+ if (d.type === DiffType.ADDED) added++;else if (d.type === DiffType.REMOVED) removed++;else if (d.type === DiffType.MODIFIED) modified++;
54
+ }
55
+ return {
56
+ added,
57
+ removed,
58
+ modified
59
+ };
60
+ }, [lineDiffs]);
61
+
62
+ // Auto-collapse runs of unchanged lines that aren't near a change. Keeps
63
+ // CONTEXT_LINES on either side of every change visible (GitHub-style), folds
64
+ // the rest into a tappable header.
65
+ const [expandedFolds, setExpandedFolds] = useState(() => new Set());
66
+
67
+ // Reset fold expansion when the user actually switches comparisons. Parent
68
+ // memoizes oldValue/newValue, so this only fires on a real change — not on
69
+ // every re-render. First run is a no-op (initial state is already empty).
70
+ const hasMountedRef = useRef(false);
71
+ useEffect(() => {
72
+ if (!hasMountedRef.current) {
73
+ hasMountedRef.current = true;
74
+ return;
75
+ }
76
+ setExpandedFolds(new Set());
77
+ }, [oldValue, newValue]);
78
+ const items = useMemo(() => buildSplitItems(lineDiffs, expandedFolds), [lineDiffs, expandedFolds]);
79
+ const toggleFold = useCallback(foldId => {
80
+ setExpandedFolds(prev => {
81
+ const next = new Set(prev);
82
+ if (next.has(foldId)) next.delete(foldId);else next.add(foldId);
83
+ return next;
84
+ });
85
+ }, []);
86
+ const keyExtractor = useCallback(item => item.kind === "row" ? `r:${item.diffIndex}` : `f:${item.foldId}`, []);
87
+ const renderSplitItem = useCallback(item => {
88
+ if (item.kind === "fold") {
89
+ return /*#__PURE__*/_jsx(FoldHeader, {
90
+ foldId: item.foldId,
91
+ count: item.count,
92
+ expanded: item.expanded,
93
+ theme: theme,
94
+ styles: dynamicStyles,
95
+ onToggle: toggleFold
96
+ });
97
+ }
98
+ return /*#__PURE__*/_jsx(DiffRow, {
99
+ diff: item.diff,
100
+ showSeparator: false,
101
+ theme: theme,
102
+ options: options,
103
+ styles: dynamicStyles
104
+ });
105
+ }, [theme, options, dynamicStyles, toggleFold]);
106
+ const renderItem = useCallback(({
107
+ item
108
+ }) => renderSplitItem(item), [renderSplitItem]);
109
+ return /*#__PURE__*/_jsxs(View, {
110
+ style: [dynamicStyles.container, theme.glowColor && {
111
+ shadowColor: theme.glowColor,
112
+ shadowOffset: {
113
+ width: 0,
114
+ height: 0
115
+ },
116
+ shadowOpacity: theme.neonIntensity && theme.neonIntensity > 0.7 ? 0.5 : theme.neonIntensity && theme.neonIntensity > 0.3 ? 0.3 : 0.1,
117
+ shadowRadius: theme.neonIntensity && theme.neonIntensity > 0.7 ? 10 : theme.neonIntensity && theme.neonIntensity > 0.3 ? 5 : 2
118
+ }],
119
+ children: [showThemeName && /*#__PURE__*/_jsxs(View, {
120
+ style: dynamicStyles.themeBadge,
121
+ children: [/*#__PURE__*/_jsx(Text, {
122
+ style: dynamicStyles.themeName,
123
+ children: theme.name
124
+ }), /*#__PURE__*/_jsx(Text, {
125
+ style: dynamicStyles.themeDescription,
126
+ children: theme.description
127
+ })]
128
+ }), /*#__PURE__*/_jsxs(View, {
129
+ style: dynamicStyles.header,
130
+ children: [/*#__PURE__*/_jsx(View, {
131
+ style: dynamicStyles.headerLeft,
132
+ children: /*#__PURE__*/_jsx(Text, {
133
+ style: dynamicStyles.headerTitle,
134
+ children: "PREV"
135
+ })
136
+ }), /*#__PURE__*/_jsx(View, {
137
+ style: dynamicStyles.divider
138
+ }), /*#__PURE__*/_jsx(View, {
139
+ style: dynamicStyles.headerRight,
140
+ children: /*#__PURE__*/_jsx(Text, {
141
+ style: dynamicStyles.headerTitle,
142
+ children: "CUR"
143
+ })
144
+ })]
145
+ }), /*#__PURE__*/_jsx(DiffSummary, {
146
+ added: summary.added,
147
+ removed: summary.removed,
148
+ modified: summary.modified,
149
+ theme: theme
150
+ }), lineDiffs.length === 0 ? /*#__PURE__*/_jsx(View, {
151
+ style: dynamicStyles.emptyState,
152
+ children: /*#__PURE__*/_jsx(Text, {
153
+ style: dynamicStyles.emptyText,
154
+ children: options.showDiffOnly ? "No differences found" : "No content to display"
155
+ })
156
+ }) : scrollEnabled ? /*#__PURE__*/_jsx(FlatList, {
157
+ style: dynamicStyles.scrollView,
158
+ contentContainerStyle: dynamicStyles.scrollContent,
159
+ data: items,
160
+ renderItem: renderItem,
161
+ keyExtractor: keyExtractor,
162
+ showsVerticalScrollIndicator: false,
163
+ initialNumToRender: 20,
164
+ maxToRenderPerBatch: 20,
165
+ windowSize: 7,
166
+ removeClippedSubviews: true
167
+ }) :
168
+ /*#__PURE__*/
169
+ // A FlatList can't virtualize inside a page ScrollView — it has no
170
+ // viewport of its own to measure against — so render the rows directly.
171
+ // Folding already keeps this list short (unchanged runs collapse).
172
+ _jsx(View, {
173
+ style: dynamicStyles.scrollContent,
174
+ children: items.map(item => /*#__PURE__*/_jsx(Fragment, {
175
+ children: renderSplitItem(item)
176
+ }, keyExtractor(item)))
177
+ })]
178
+ });
179
+ }
180
+
181
+ // Number of context lines kept visible on either side of every change.
182
+ const CONTEXT_LINES = 3;
183
+ // Don't bother folding tiny gaps — toggling them is more friction than scroll.
184
+ const MIN_FOLD_SIZE = 4;
185
+ function buildSplitItems(lineDiffs, expandedFolds) {
186
+ const n = lineDiffs.length;
187
+ if (n === 0) return [];
188
+
189
+ // Mark every line that should always be visible: any change, plus
190
+ // CONTEXT_LINES on either side of it.
191
+ const visible = new Array(n).fill(false);
192
+ for (let i = 0; i < n; i++) {
193
+ if (lineDiffs[i].type !== DiffType.DEFAULT) {
194
+ const start = Math.max(0, i - CONTEXT_LINES);
195
+ const end = Math.min(n - 1, i + CONTEXT_LINES);
196
+ for (let k = start; k <= end; k++) visible[k] = true;
197
+ }
198
+ }
199
+ const items = [];
200
+ let foldId = 0;
201
+ let i = 0;
202
+ while (i < n) {
203
+ if (visible[i]) {
204
+ items.push({
205
+ kind: "row",
206
+ diff: lineDiffs[i],
207
+ diffIndex: i
208
+ });
209
+ i++;
210
+ continue;
211
+ }
212
+ let j = i;
213
+ while (j < n && !visible[j]) j++;
214
+ const runLength = j - i;
215
+ if (runLength < MIN_FOLD_SIZE) {
216
+ for (let k = i; k < j; k++) {
217
+ items.push({
218
+ kind: "row",
219
+ diff: lineDiffs[k],
220
+ diffIndex: k
221
+ });
222
+ }
223
+ } else {
224
+ const id = foldId++;
225
+ const expanded = expandedFolds.has(id);
226
+ items.push({
227
+ kind: "fold",
228
+ foldId: id,
229
+ count: runLength,
230
+ expanded
231
+ });
232
+ if (expanded) {
233
+ for (let k = i; k < j; k++) {
234
+ items.push({
235
+ kind: "row",
236
+ diff: lineDiffs[k],
237
+ diffIndex: k
238
+ });
239
+ }
240
+ }
241
+ }
242
+ i = j;
243
+ }
244
+ return items;
245
+ }
246
+ const FoldHeader = /*#__PURE__*/memo(function FoldHeader({
247
+ foldId,
248
+ count,
249
+ expanded,
250
+ theme,
251
+ styles,
252
+ onToggle
253
+ }) {
254
+ return /*#__PURE__*/_jsx(TouchableOpacity, {
255
+ style: [styles.foldHeader, {
256
+ backgroundColor: theme.separatorBackground
257
+ }],
258
+ onPress: () => onToggle(foldId),
259
+ activeOpacity: 0.7,
260
+ children: /*#__PURE__*/_jsx(Text, {
261
+ style: [styles.foldHeaderText, {
262
+ color: theme.separatorText
263
+ }],
264
+ children: expanded ? `▼ ${count} unchanged line${count === 1 ? "" : "s"} — tap to hide` : `▶ ${count} unchanged line${count === 1 ? "" : "s"} — tap to expand`
265
+ })
266
+ });
267
+ });
268
+ const DiffRow = /*#__PURE__*/memo(function DiffRow({
269
+ diff,
270
+ showSeparator,
271
+ theme,
272
+ options,
273
+ styles
274
+ }) {
275
+ const isRemoved = diff.type === DiffType.REMOVED;
276
+ const isAdded = diff.type === DiffType.ADDED;
277
+ const isModified = diff.type === DiffType.MODIFIED;
278
+ const isDefault = diff.type === DiffType.DEFAULT;
279
+ return /*#__PURE__*/_jsxs(Fragment, {
280
+ children: [showSeparator && /*#__PURE__*/_jsx(View, {
281
+ style: styles.separator,
282
+ children: /*#__PURE__*/_jsx(Text, {
283
+ style: styles.separatorText,
284
+ children: "\u2022 \u2022 \u2022"
285
+ })
286
+ }), /*#__PURE__*/_jsxs(View, {
287
+ style: styles.row,
288
+ children: [/*#__PURE__*/_jsx(View, {
289
+ style: styles.leftSide,
290
+ children: isRemoved || isModified || isDefault ? /*#__PURE__*/_jsx(LineSide, {
291
+ lineNumber: diff.leftLineNumber,
292
+ content: diff.leftContent,
293
+ type: isModified ? DiffType.REMOVED : diff.type,
294
+ marker: isRemoved || isModified ? "-" : " ",
295
+ theme: theme,
296
+ options: options,
297
+ styles: styles
298
+ }) : /*#__PURE__*/_jsx(EmptyLineSide, {
299
+ options: options,
300
+ styles: styles
301
+ })
302
+ }), /*#__PURE__*/_jsx(View, {
303
+ style: styles.centerDivider
304
+ }), /*#__PURE__*/_jsx(View, {
305
+ style: styles.rightSide,
306
+ children: isAdded || isModified || isDefault ? /*#__PURE__*/_jsx(LineSide, {
307
+ lineNumber: diff.rightLineNumber,
308
+ content: diff.rightContent,
309
+ type: isModified ? DiffType.ADDED : diff.type,
310
+ marker: isAdded || isModified ? "+" : " ",
311
+ theme: theme,
312
+ options: options,
313
+ styles: styles
314
+ }) : /*#__PURE__*/_jsx(EmptyLineSide, {
315
+ options: options,
316
+ styles: styles
317
+ })
318
+ })]
319
+ })]
320
+ });
321
+ });
322
+ function LineSide({
323
+ lineNumber,
324
+ content,
325
+ type,
326
+ marker,
327
+ theme,
328
+ options,
329
+ styles
330
+ }) {
331
+ const colors = getDiffColors(type, theme);
332
+ return /*#__PURE__*/_jsxs(Fragment, {
333
+ children: [!options.hideLineNumbers && /*#__PURE__*/_jsx(View, {
334
+ style: [styles.gutter, {
335
+ backgroundColor: theme.lineNumberBackground
336
+ }],
337
+ children: /*#__PURE__*/_jsx(Text, {
338
+ style: styles.lineNumber,
339
+ children: lineNumber || " "
340
+ })
341
+ }), /*#__PURE__*/_jsx(View, {
342
+ style: [styles.marker, {
343
+ backgroundColor: colors.markerBg
344
+ }],
345
+ children: /*#__PURE__*/_jsx(Text, {
346
+ style: [styles.markerText, {
347
+ color: theme.markerText
348
+ }],
349
+ children: marker
350
+ })
351
+ }), /*#__PURE__*/_jsx(View, {
352
+ style: [styles.contentCell, {
353
+ backgroundColor: colors.background
354
+ }],
355
+ children: /*#__PURE__*/_jsx(Text, {
356
+ style: [styles.content, {
357
+ color: colors.text
358
+ }],
359
+ children: Array.isArray(content) ? content.map((word, idx) => {
360
+ let backgroundColor = "transparent";
361
+ if (word.type === DiffType.ADDED) {
362
+ backgroundColor = theme.addedWordHighlight;
363
+ } else if (word.type === DiffType.REMOVED) {
364
+ backgroundColor = theme.removedWordHighlight;
365
+ }
366
+ return /*#__PURE__*/_jsx(Text, {
367
+ style: [styles.wordDiff, {
368
+ backgroundColor
369
+ }],
370
+ children: word.value
371
+ }, idx);
372
+ }) : content || " "
373
+ })
374
+ })]
375
+ });
376
+ }
377
+ function EmptyLineSide({
378
+ options,
379
+ styles
380
+ }) {
381
+ return /*#__PURE__*/_jsxs(Fragment, {
382
+ children: [!options.hideLineNumbers && /*#__PURE__*/_jsx(View, {
383
+ style: [styles.gutter, styles.emptyGutter],
384
+ children: /*#__PURE__*/_jsx(Text, {
385
+ style: styles.lineNumber,
386
+ children: " "
387
+ })
388
+ }), /*#__PURE__*/_jsx(View, {
389
+ style: [styles.marker, styles.emptyMarker],
390
+ children: /*#__PURE__*/_jsx(Text, {
391
+ style: styles.markerText,
392
+ children: " "
393
+ })
394
+ }), /*#__PURE__*/_jsx(View, {
395
+ style: [styles.contentCell, styles.emptyContent],
396
+ children: /*#__PURE__*/_jsx(Text, {
397
+ style: styles.content,
398
+ children: " "
399
+ })
400
+ })]
401
+ });
402
+ }
403
+ function getDiffColors(type, theme) {
404
+ switch (type) {
405
+ case DiffType.ADDED:
406
+ return {
407
+ background: theme.addedBackground,
408
+ text: theme.addedText,
409
+ markerBg: theme.markerAddedBackground
410
+ };
411
+ case DiffType.REMOVED:
412
+ return {
413
+ background: theme.removedBackground,
414
+ text: theme.removedText,
415
+ markerBg: theme.markerRemovedBackground
416
+ };
417
+ case DiffType.DEFAULT:
418
+ default:
419
+ return {
420
+ background: theme.unchangedBackground,
421
+ text: theme.unchangedText,
422
+ markerBg: "transparent"
423
+ };
424
+ }
425
+ }
426
+
427
+ // Create dynamic styles based on theme
428
+ function createDynamicStyles(theme, scrollEnabled) {
429
+ return StyleSheet.create({
430
+ container: {
431
+ // Fixed pane only when the rows scroll themselves; inline mode sizes to
432
+ // content so the page scroll reveals it.
433
+ ...(scrollEnabled ? {
434
+ height: 400
435
+ } : null),
436
+ backgroundColor: theme.background,
437
+ borderRadius: 8,
438
+ overflow: "hidden",
439
+ borderWidth: 1,
440
+ borderColor: theme.borderColor
441
+ },
442
+ themeBadge: {
443
+ backgroundColor: theme.panelBackground,
444
+ paddingHorizontal: 12,
445
+ paddingVertical: 8,
446
+ borderBottomWidth: 1,
447
+ borderBottomColor: theme.borderColor
448
+ },
449
+ themeName: {
450
+ fontSize: 12,
451
+ fontWeight: "700",
452
+ color: theme.accentColor || theme.unchangedText,
453
+ fontFamily: "monospace",
454
+ letterSpacing: 0.5
455
+ },
456
+ themeDescription: {
457
+ fontSize: 10,
458
+ color: theme.unchangedText,
459
+ fontFamily: "monospace",
460
+ marginTop: 2,
461
+ opacity: 0.8
462
+ },
463
+ header: {
464
+ flexDirection: "row",
465
+ backgroundColor: theme.headerBackground,
466
+ borderBottomWidth: 1,
467
+ borderBottomColor: theme.borderColor
468
+ },
469
+ headerLeft: {
470
+ flex: 1,
471
+ paddingVertical: 8,
472
+ paddingHorizontal: 12
473
+ },
474
+ headerRight: {
475
+ flex: 1,
476
+ paddingVertical: 8,
477
+ paddingHorizontal: 12
478
+ },
479
+ headerTitle: {
480
+ fontSize: 10,
481
+ fontWeight: "700",
482
+ color: theme.accentColor || theme.unchangedText,
483
+ fontFamily: "monospace",
484
+ letterSpacing: 0.5,
485
+ textTransform: "uppercase"
486
+ },
487
+ divider: {
488
+ width: 1,
489
+ backgroundColor: theme.dividerColor
490
+ },
491
+ scrollView: {
492
+ flex: 1
493
+ },
494
+ scrollContent: {
495
+ paddingBottom: 10
496
+ },
497
+ row: {
498
+ flexDirection: "row",
499
+ minHeight: 20
500
+ },
501
+ leftSide: {
502
+ flex: 1,
503
+ flexDirection: "row"
504
+ },
505
+ rightSide: {
506
+ flex: 1,
507
+ flexDirection: "row"
508
+ },
509
+ centerDivider: {
510
+ width: 1,
511
+ backgroundColor: theme.dividerColor
512
+ },
513
+ gutter: {
514
+ width: 35,
515
+ paddingHorizontal: 4,
516
+ justifyContent: "center",
517
+ alignItems: "flex-end",
518
+ backgroundColor: theme.lineNumberBackground,
519
+ borderRightWidth: 1,
520
+ borderRightColor: theme.lineNumberBorder
521
+ },
522
+ emptyGutter: {
523
+ backgroundColor: theme.contextBackground
524
+ },
525
+ lineNumber: {
526
+ fontSize: 9,
527
+ fontFamily: "monospace",
528
+ color: theme.lineNumberText
529
+ },
530
+ marker: {
531
+ width: 20,
532
+ justifyContent: "center",
533
+ alignItems: "center"
534
+ },
535
+ emptyMarker: {
536
+ backgroundColor: theme.contextBackground
537
+ },
538
+ markerText: {
539
+ fontSize: 10,
540
+ fontFamily: "monospace",
541
+ fontWeight: "600"
542
+ },
543
+ contentCell: {
544
+ flex: 1,
545
+ paddingHorizontal: 8,
546
+ paddingVertical: 2
547
+ },
548
+ emptyContent: {
549
+ backgroundColor: theme.contextBackground
550
+ },
551
+ content: {
552
+ fontSize: 10,
553
+ fontFamily: "monospace",
554
+ lineHeight: 16
555
+ },
556
+ wordDiff: {
557
+ fontSize: 10,
558
+ fontFamily: "monospace"
559
+ },
560
+ summaryBar: {
561
+ flexDirection: "row",
562
+ alignItems: "center",
563
+ justifyContent: "center",
564
+ gap: 16,
565
+ paddingVertical: 6,
566
+ backgroundColor: theme.summaryBackground,
567
+ borderTopWidth: 1,
568
+ borderTopColor: theme.borderColor
569
+ },
570
+ summaryItem: {
571
+ flexDirection: "row",
572
+ alignItems: "center"
573
+ },
574
+ summaryText: {
575
+ fontSize: 10,
576
+ fontFamily: "monospace",
577
+ fontWeight: "700"
578
+ },
579
+ separator: {
580
+ height: 20,
581
+ justifyContent: "center",
582
+ alignItems: "center",
583
+ backgroundColor: theme.separatorBackground
584
+ },
585
+ separatorText: {
586
+ fontSize: 8,
587
+ color: theme.separatorText,
588
+ fontFamily: "monospace",
589
+ letterSpacing: 2
590
+ },
591
+ foldHeader: {
592
+ paddingVertical: 6,
593
+ paddingHorizontal: 12,
594
+ alignItems: "center",
595
+ justifyContent: "center",
596
+ borderTopWidth: 1,
597
+ borderBottomWidth: 1,
598
+ borderColor: theme.borderColor
599
+ },
600
+ foldHeaderText: {
601
+ fontSize: 10,
602
+ fontFamily: "monospace",
603
+ letterSpacing: 0.5
604
+ },
605
+ emptyState: {
606
+ padding: 40,
607
+ alignItems: "center",
608
+ justifyContent: "center"
609
+ },
610
+ emptyText: {
611
+ fontSize: 11,
612
+ color: theme.emptyStateText,
613
+ fontStyle: "italic",
614
+ fontFamily: "monospace"
615
+ }
616
+ });
617
+ }
@@ -35,4 +35,10 @@ export { SplitDiffViewer } from "./SplitDiffViewer.js";
35
35
  export { DiffSummary } from "./DiffSummary.js";
36
36
  // Diff utilities and themes
37
37
  export { computeLineDiff, DiffType, ABSENT_VALUE, isAbsent } from "./lineDiff.js";
38
- export { diffThemes, gitClassicTheme, devToolsDefaultTheme } from "./diffThemes.js";
38
+ export { diffThemes, gitClassicTheme, devToolsDefaultTheme } from "./diffThemes.js";
39
+
40
+ // Bounded diff kernel + render normalizer + size probe (verdicts/badges — not rendering)
41
+ export { objectDiff, boundedDeepEqual, toDiffable, jsonLikelyLargerThan } from "./objectDiff.js";
42
+
43
+ // The production split renderer (folding + virtualization), promoted from storage
44
+ export { ThemedSplitView } from "./ThemedSplitView.js";