@buoy-gg/shared-ui 7.0.4 → 7.0.6

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 (191) hide show
  1. package/lib/commonjs/colors/index.js +49 -0
  2. package/lib/commonjs/dataViewer/DataTreeActionBar.js +389 -0
  3. package/lib/commonjs/dataViewer/DataTreeActionDock.js +222 -0
  4. package/lib/commonjs/dataViewer/DataViewer.js +18 -3
  5. package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +227 -28
  6. package/lib/commonjs/dataViewer/index.js +88 -0
  7. package/lib/commonjs/dataViewer/jsonOps.js +320 -0
  8. package/lib/commonjs/dataViewer/lineDiff.js +285 -83
  9. package/lib/commonjs/dataViewer/tree/TreeDiffViewer.js +20 -5
  10. package/lib/commonjs/dataViewer/treeActions.js +167 -0
  11. package/lib/commonjs/env/EnvironmentSelector.js +2 -2
  12. package/lib/commonjs/env/EnvironmentSelectorInline.js +2 -2
  13. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  14. package/lib/commonjs/icons/index.js +79 -3
  15. package/lib/commonjs/icons/lucide-icons-original-full.js +5 -1
  16. package/lib/commonjs/icons/lucide-icons.js +59 -2
  17. package/lib/commonjs/index.js +35 -0
  18. package/lib/commonjs/settings/components/BubbleSettingsSection.js +21 -19
  19. package/lib/commonjs/storage/devToolsStorageKeys.js +6 -3
  20. package/lib/commonjs/ui/components/Badge.js +2 -0
  21. package/lib/commonjs/ui/components/CompactRow.js +7 -0
  22. package/lib/commonjs/ui/components/CopyButton.js +27 -4
  23. package/lib/commonjs/ui/components/DevToolsCard.js +11 -0
  24. package/lib/commonjs/ui/components/DynamicFilterView.js +8 -1
  25. package/lib/commonjs/ui/components/EmptyState.js +12 -5
  26. package/lib/commonjs/ui/components/ErrorBoundary.js +3 -2
  27. package/lib/commonjs/ui/components/EventHistoryViewer/EventHistoryViewer.js +63 -11
  28. package/lib/commonjs/ui/components/ExpandableSection.js +4 -3
  29. package/lib/commonjs/ui/components/ExpandableSectionHeader.js +2 -1
  30. package/lib/commonjs/ui/components/FilterViewPattern.js +8 -7
  31. package/lib/commonjs/ui/components/ListItem.js +7 -5
  32. package/lib/commonjs/ui/components/PowerToggleButton.js +4 -1
  33. package/lib/commonjs/ui/components/TabSelector.js +3 -0
  34. package/lib/commonjs/ui/console/CyberpunkConsoleSection.js +1 -1
  35. package/lib/commonjs/ui/console/GalaxyButton.js +1 -1
  36. package/lib/commonjs/ui/gameUI/components/GameUICompactStats.js +2 -2
  37. package/lib/commonjs/ui/gameUI/constants/gameUIColors.js +28 -6
  38. package/lib/commonjs/ui/gameUI/constants/macOSDesignSystemColors.js +22 -4
  39. package/lib/commonjs/utils/index.js +25 -0
  40. package/lib/commonjs/utils/reloadApp.js +144 -0
  41. package/lib/module/colors/index.js +21 -0
  42. package/lib/module/dataViewer/DataTreeActionBar.js +384 -0
  43. package/lib/module/dataViewer/DataTreeActionDock.js +218 -0
  44. package/lib/module/dataViewer/DataViewer.js +18 -3
  45. package/lib/module/dataViewer/VirtualizedDataExplorer.js +228 -30
  46. package/lib/module/dataViewer/index.js +16 -1
  47. package/lib/module/dataViewer/jsonOps.js +311 -0
  48. package/lib/module/dataViewer/lineDiff.js +284 -82
  49. package/lib/module/dataViewer/tree/TreeDiffViewer.js +20 -5
  50. package/lib/module/dataViewer/treeActions.js +161 -0
  51. package/lib/module/env/EnvironmentSelector.js +2 -2
  52. package/lib/module/env/EnvironmentSelectorInline.js +2 -2
  53. package/lib/module/hooks/safe-area-impl.js +1 -1
  54. package/lib/module/icons/index.js +10 -1
  55. package/lib/module/icons/lucide-icons-original-full.js +5 -1
  56. package/lib/module/icons/lucide-icons.js +56 -0
  57. package/lib/module/index.js +4 -0
  58. package/lib/module/settings/components/BubbleSettingsSection.js +21 -19
  59. package/lib/module/storage/devToolsStorageKeys.js +6 -3
  60. package/lib/module/ui/components/Badge.js +2 -0
  61. package/lib/module/ui/components/CompactRow.js +7 -0
  62. package/lib/module/ui/components/CopyButton.js +29 -6
  63. package/lib/module/ui/components/DevToolsCard.js +11 -0
  64. package/lib/module/ui/components/DynamicFilterView.js +8 -1
  65. package/lib/module/ui/components/EmptyState.js +12 -5
  66. package/lib/module/ui/components/ErrorBoundary.js +3 -2
  67. package/lib/module/ui/components/EventHistoryViewer/EventHistoryViewer.js +63 -11
  68. package/lib/module/ui/components/ExpandableSection.js +4 -3
  69. package/lib/module/ui/components/ExpandableSectionHeader.js +2 -1
  70. package/lib/module/ui/components/FilterViewPattern.js +9 -7
  71. package/lib/module/ui/components/ListItem.js +7 -5
  72. package/lib/module/ui/components/PowerToggleButton.js +4 -1
  73. package/lib/module/ui/components/TabSelector.js +3 -0
  74. package/lib/module/ui/console/CyberpunkConsoleSection.js +1 -1
  75. package/lib/module/ui/console/GalaxyButton.js +1 -1
  76. package/lib/module/ui/gameUI/components/GameUICompactStats.js +2 -2
  77. package/lib/module/ui/gameUI/constants/gameUIColors.js +27 -6
  78. package/lib/module/ui/gameUI/constants/macOSDesignSystemColors.js +19 -2
  79. package/lib/module/utils/index.js +1 -0
  80. package/lib/module/utils/reloadApp.js +137 -0
  81. package/lib/typescript/commonjs/colors/index.d.ts +22 -0
  82. package/lib/typescript/commonjs/colors/index.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/dataViewer/DataTreeActionBar.d.ts +53 -0
  84. package/lib/typescript/commonjs/dataViewer/DataTreeActionBar.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts +33 -0
  86. package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/dataViewer/DataViewer.d.ts +14 -1
  88. package/lib/typescript/commonjs/dataViewer/DataViewer.d.ts.map +1 -1
  89. package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts +30 -1
  90. package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  91. package/lib/typescript/commonjs/dataViewer/__tests__/jsonOps.test.d.ts +19 -0
  92. package/lib/typescript/commonjs/dataViewer/__tests__/jsonOps.test.d.ts.map +1 -0
  93. package/lib/typescript/commonjs/dataViewer/index.d.ts +5 -1
  94. package/lib/typescript/commonjs/dataViewer/index.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/dataViewer/jsonOps.d.ts +124 -0
  96. package/lib/typescript/commonjs/dataViewer/jsonOps.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/dataViewer/lineDiff.d.ts +27 -0
  98. package/lib/typescript/commonjs/dataViewer/lineDiff.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts +8 -1
  100. package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/dataViewer/treeActions.d.ts +83 -0
  102. package/lib/typescript/commonjs/dataViewer/treeActions.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  104. package/lib/typescript/commonjs/icons/index.d.ts +6 -1
  105. package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/icons/lucide-icons-original-full.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/icons/lucide-icons.d.ts +9 -0
  108. package/lib/typescript/commonjs/icons/lucide-icons.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/index.d.ts +2 -1
  110. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/settings/components/BubbleSettingsSection.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +5 -1
  113. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/components/CopyButton.d.ts +7 -1
  115. package/lib/typescript/commonjs/ui/components/CopyButton.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/ui/components/EmptyState.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/components/ErrorBoundary.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/EventHistoryViewer.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/types.d.ts +23 -7
  120. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/types.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/ui/components/ExpandableSection.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/ui/components/ExpandableSectionHeader.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/components/FilterViewPattern.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/components/ListItem.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts +36 -30
  126. package/lib/typescript/commonjs/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/gameUI/constants/macOSDesignSystemColors.d.ts +5 -0
  128. package/lib/typescript/commonjs/ui/gameUI/constants/macOSDesignSystemColors.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/utils/index.d.ts +1 -0
  130. package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/utils/reloadApp.d.ts +68 -0
  132. package/lib/typescript/commonjs/utils/reloadApp.d.ts.map +1 -0
  133. package/lib/typescript/module/colors/index.d.ts +22 -0
  134. package/lib/typescript/module/colors/index.d.ts.map +1 -0
  135. package/lib/typescript/module/dataViewer/DataTreeActionBar.d.ts +53 -0
  136. package/lib/typescript/module/dataViewer/DataTreeActionBar.d.ts.map +1 -0
  137. package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts +33 -0
  138. package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts.map +1 -0
  139. package/lib/typescript/module/dataViewer/DataViewer.d.ts +14 -1
  140. package/lib/typescript/module/dataViewer/DataViewer.d.ts.map +1 -1
  141. package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts +30 -1
  142. package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  143. package/lib/typescript/module/dataViewer/__tests__/jsonOps.test.d.ts +19 -0
  144. package/lib/typescript/module/dataViewer/__tests__/jsonOps.test.d.ts.map +1 -0
  145. package/lib/typescript/module/dataViewer/index.d.ts +5 -1
  146. package/lib/typescript/module/dataViewer/index.d.ts.map +1 -1
  147. package/lib/typescript/module/dataViewer/jsonOps.d.ts +124 -0
  148. package/lib/typescript/module/dataViewer/jsonOps.d.ts.map +1 -0
  149. package/lib/typescript/module/dataViewer/lineDiff.d.ts +27 -0
  150. package/lib/typescript/module/dataViewer/lineDiff.d.ts.map +1 -1
  151. package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts +8 -1
  152. package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  153. package/lib/typescript/module/dataViewer/treeActions.d.ts +83 -0
  154. package/lib/typescript/module/dataViewer/treeActions.d.ts.map +1 -0
  155. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  156. package/lib/typescript/module/icons/index.d.ts +6 -1
  157. package/lib/typescript/module/icons/index.d.ts.map +1 -1
  158. package/lib/typescript/module/icons/lucide-icons-original-full.d.ts.map +1 -1
  159. package/lib/typescript/module/icons/lucide-icons.d.ts +9 -0
  160. package/lib/typescript/module/icons/lucide-icons.d.ts.map +1 -1
  161. package/lib/typescript/module/index.d.ts +2 -1
  162. package/lib/typescript/module/index.d.ts.map +1 -1
  163. package/lib/typescript/module/settings/components/BubbleSettingsSection.d.ts.map +1 -1
  164. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +5 -1
  165. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  166. package/lib/typescript/module/ui/components/CopyButton.d.ts +7 -1
  167. package/lib/typescript/module/ui/components/CopyButton.d.ts.map +1 -1
  168. package/lib/typescript/module/ui/components/EmptyState.d.ts.map +1 -1
  169. package/lib/typescript/module/ui/components/ErrorBoundary.d.ts.map +1 -1
  170. package/lib/typescript/module/ui/components/EventHistoryViewer/EventHistoryViewer.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/components/EventHistoryViewer/types.d.ts +23 -7
  172. package/lib/typescript/module/ui/components/EventHistoryViewer/types.d.ts.map +1 -1
  173. package/lib/typescript/module/ui/components/ExpandableSection.d.ts.map +1 -1
  174. package/lib/typescript/module/ui/components/ExpandableSectionHeader.d.ts.map +1 -1
  175. package/lib/typescript/module/ui/components/FilterViewPattern.d.ts.map +1 -1
  176. package/lib/typescript/module/ui/components/ListItem.d.ts.map +1 -1
  177. package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts +36 -30
  178. package/lib/typescript/module/ui/gameUI/constants/gameUIColors.d.ts.map +1 -1
  179. package/lib/typescript/module/ui/gameUI/constants/macOSDesignSystemColors.d.ts +5 -0
  180. package/lib/typescript/module/ui/gameUI/constants/macOSDesignSystemColors.d.ts.map +1 -1
  181. package/lib/typescript/module/utils/index.d.ts +1 -0
  182. package/lib/typescript/module/utils/index.d.ts.map +1 -1
  183. package/lib/typescript/module/utils/reloadApp.d.ts +68 -0
  184. package/lib/typescript/module/utils/reloadApp.d.ts.map +1 -0
  185. package/package.json +15 -4
  186. package/lib/commonjs/icons/ImageOverlayIcon.js +0 -142
  187. package/lib/module/icons/ImageOverlayIcon.js +0 -137
  188. package/lib/typescript/commonjs/icons/ImageOverlayIcon.d.ts +0 -14
  189. package/lib/typescript/commonjs/icons/ImageOverlayIcon.d.ts.map +0 -1
  190. package/lib/typescript/module/icons/ImageOverlayIcon.d.ts +0 -14
  191. package/lib/typescript/module/icons/ImageOverlayIcon.d.ts.map +0 -1
@@ -3,8 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.DiffType = void 0;
6
+ exports.DiffType = exports.ABSENT_VALUE = void 0;
7
7
  exports.computeLineDiff = computeLineDiff;
8
+ exports.isAbsent = isAbsent;
8
9
  /**
9
10
  * Line-by-line diff computation for React Native
10
11
  *
@@ -18,6 +19,23 @@ let DiffType = exports.DiffType = /*#__PURE__*/function (DiffType) {
18
19
  DiffType["MODIFIED"] = "modified";
19
20
  return DiffType;
20
21
  }({});
22
+ /**
23
+ * Marks one side of a diff as "this value did not exist".
24
+ *
25
+ * Deliberately distinct from `null`, `[]`, `{}` and `""` — those are real
26
+ * stored values and diff normally. An absent side contributes zero lines, so a
27
+ * key that was just created reads as purely added rather than as an edit of the
28
+ * literal text `null`.
29
+ *
30
+ * `Symbol.for` so the identity holds even if a bundler ends up with two copies
31
+ * of this module.
32
+ */
33
+ const ABSENT_VALUE = exports.ABSENT_VALUE = Symbol.for("buoy.diff.absent");
34
+
35
+ /** True when a diff side represents a value that did not exist. */
36
+ function isAbsent(value) {
37
+ return value === ABSENT_VALUE;
38
+ }
21
39
  /**
22
40
  * Compute diff based on method - for word-level diff within lines
23
41
  */
@@ -157,6 +175,10 @@ function computeDiffByMethod(oldStr, newStr, method) {
157
175
  * Convert object to formatted JSON lines
158
176
  */
159
177
  function objectToLines(obj) {
178
+ // A value that never existed has no text at all — not the word "null".
179
+ if (isAbsent(obj)) {
180
+ return [];
181
+ }
160
182
  if (obj === null || obj === undefined) {
161
183
  return [String(obj)];
162
184
  }
@@ -240,63 +262,265 @@ function filterDiffWithContext(diffs, contextLines) {
240
262
  }
241
263
 
242
264
  /**
243
- * Compare lines based on method
265
+ * Normalize a line into the key used for matching.
266
+ *
267
+ * Display always uses the raw JSON; only matching sees this. `trimmedLines`
268
+ * drops indentation, and `ignoreTrailingComma` drops the trailing comma so
269
+ * pushing one item onto an array doesn't report the previous last item as
270
+ * modified.
244
271
  */
245
- function compareLinesWithMethod(line1, line2, method) {
246
- switch (method) {
247
- case "trimmedLines":
248
- return line1.trim() === line2.trim();
249
- case "chars":
250
- case "words":
251
- case "lines":
252
- default:
253
- return line1 === line2;
272
+ function normalizeCompareKey(line, compareMethod, ignoreTrailingComma) {
273
+ const base = compareMethod === "trimmedLines" ? line.trim() : line;
274
+ return ignoreTrailingComma ? base.replace(/,\s*$/, "") : base;
275
+ }
276
+
277
+ /**
278
+ * The original heuristic: lines that start alike are an edit of one line rather
279
+ * than an unrelated removal plus addition. Only ever applied inside a hunk the
280
+ * line matcher already proved unmatched, so it can no longer pair lines that
281
+ * merely shifted position.
282
+ */
283
+ function looksLikeModification(oldLine, newLine) {
284
+ const oldTrimmed = oldLine.trim();
285
+ const newTrimmed = newLine.trim();
286
+ return !!oldTrimmed && !!newTrimmed && (oldTrimmed.startsWith(newTrimmed.substring(0, 3)) || newTrimmed.startsWith(oldTrimmed.substring(0, 3)));
287
+ }
288
+ /**
289
+ * Myers is O(ND) in the number of edits, so near-identical values (the common
290
+ * case — one array push) are close to free and only wildly different values get
291
+ * expensive. Past these bounds we fall back to the pairwise walk.
292
+ */
293
+ const MYERS_MAX_TOTAL_LINES = 8000;
294
+ const MYERS_MAX_EDIT_DISTANCE = 1024;
295
+
296
+ /**
297
+ * Myers' O(ND) diff ("An O(ND) Difference Algorithm and Its Variations", 1986)
298
+ * over line keys — the same algorithm git and `diff` use.
299
+ *
300
+ * Returns null when the input is too large or too dissimilar to be worth it,
301
+ * which tells the caller to use the fallback walk.
302
+ */
303
+ function myersLineOps(a, b) {
304
+ const n = a.length;
305
+ const m = b.length;
306
+ if (n + m > MYERS_MAX_TOTAL_LINES) return null;
307
+ if (n === 0 && m === 0) return [];
308
+ const max = n + m;
309
+ // +1 of padding on each side so the k-1 / k+1 probes at the frontier stay in
310
+ // bounds without clamping (clamping would corrupt the index arithmetic).
311
+ const offset = max + 1;
312
+ const v = new Int32Array(2 * max + 3);
313
+ const maxD = Math.min(max, MYERS_MAX_EDIT_DISTANCE);
314
+
315
+ // Frontier snapshot per depth. Only k in [-(d+1), d+1] is ever read back, so
316
+ // each snapshot is O(d) and the whole trace is O(D^2) — not O(D * max).
317
+ const trace = [];
318
+ for (let d = 0; d <= maxD; d++) {
319
+ trace.push(v.slice(offset - (d + 1), offset + d + 2));
320
+ for (let k = -d; k <= d; k += 2) {
321
+ let x;
322
+ if (k === -d || k !== d && v[k - 1 + offset] < v[k + 1 + offset]) {
323
+ x = v[k + 1 + offset];
324
+ } else {
325
+ x = v[k - 1 + offset] + 1;
326
+ }
327
+ let y = x - k;
328
+
329
+ // Follow the diagonal as far as the lines keep matching.
330
+ while (x < n && y < m && a[x] === b[y]) {
331
+ x++;
332
+ y++;
333
+ }
334
+ v[k + offset] = x;
335
+ if (x >= n && y >= m) {
336
+ return backtrackOps(trace, n, m);
337
+ }
338
+ }
254
339
  }
340
+ return null;
255
341
  }
256
342
 
257
343
  /**
258
- * Compute line-by-line diff between two objects
344
+ * Walk the recorded frontiers backwards to recover the edit script.
259
345
  */
260
- function computeLineDiff(oldValue, newValue, options = {}) {
261
- const {
262
- compareMethod = "words",
263
- disableWordDiff = false,
264
- showDiffOnly = false,
265
- contextLines = 3
266
- } = options;
267
- const oldLines = objectToLines(oldValue);
268
- const newLines = objectToLines(newValue);
346
+ function backtrackOps(trace, n, m) {
347
+ const ops = [];
348
+ let x = n;
349
+ let y = m;
350
+ for (let d = trace.length - 1; d >= 0; d--) {
351
+ const snapshot = trace[d];
352
+ // Snapshot d covers k in [-(d+1), d+1], so k=0 sits at index d+1.
353
+ const base = d + 1;
354
+ const k = x - y;
355
+ let prevK;
356
+ if (k === -d || k !== d && snapshot[k - 1 + base] < snapshot[k + 1 + base]) {
357
+ prevK = k + 1;
358
+ } else {
359
+ prevK = k - 1;
360
+ }
361
+ const prevX = snapshot[prevK + base];
362
+ const prevY = prevX - prevK;
363
+ while (x > prevX && y > prevY) {
364
+ ops.push({
365
+ kind: "eq",
366
+ i: x - 1,
367
+ j: y - 1
368
+ });
369
+ x--;
370
+ y--;
371
+ }
372
+ if (d > 0) {
373
+ if (x === prevX) {
374
+ ops.push({
375
+ kind: "ins",
376
+ j: y - 1
377
+ });
378
+ } else {
379
+ ops.push({
380
+ kind: "del",
381
+ i: x - 1
382
+ });
383
+ }
384
+ }
385
+ x = prevX;
386
+ y = prevY;
387
+ }
388
+ ops.reverse();
389
+ return ops;
390
+ }
391
+
392
+ /**
393
+ * Emit one hunk of unmatched lines. Removals and additions are paired off in
394
+ * order and become MODIFIED rows when they look like an edit of the same line;
395
+ * everything left over stays a one-sided removal or addition.
396
+ */
397
+ function emitHunk(result, dels, inss, oldLines, newLines, compareMethod, disableWordDiff) {
398
+ const pairs = Math.min(dels.length, inss.length);
399
+ for (let k = 0; k < pairs; k++) {
400
+ const i = dels[k];
401
+ const j = inss[k];
402
+ if (!looksLikeModification(oldLines[i], newLines[j])) {
403
+ result.push({
404
+ leftLineNumber: i + 1,
405
+ type: DiffType.REMOVED,
406
+ leftContent: oldLines[i],
407
+ leftRaw: oldLines[i]
408
+ });
409
+ result.push({
410
+ rightLineNumber: j + 1,
411
+ type: DiffType.ADDED,
412
+ rightContent: newLines[j],
413
+ rightRaw: newLines[j]
414
+ });
415
+ continue;
416
+ }
417
+ if (!disableWordDiff && compareMethod !== "lines") {
418
+ const wordDiff = computeDiffByMethod(oldLines[i], newLines[j], compareMethod);
419
+ result.push({
420
+ leftLineNumber: i + 1,
421
+ rightLineNumber: j + 1,
422
+ type: DiffType.MODIFIED,
423
+ leftContent: wordDiff.left,
424
+ rightContent: wordDiff.right,
425
+ leftRaw: oldLines[i],
426
+ rightRaw: newLines[j]
427
+ });
428
+ } else {
429
+ result.push({
430
+ leftLineNumber: i + 1,
431
+ rightLineNumber: j + 1,
432
+ type: DiffType.MODIFIED,
433
+ leftContent: oldLines[i],
434
+ rightContent: newLines[j],
435
+ leftRaw: oldLines[i],
436
+ rightRaw: newLines[j]
437
+ });
438
+ }
439
+ }
440
+ for (let k = pairs; k < dels.length; k++) {
441
+ const i = dels[k];
442
+ result.push({
443
+ leftLineNumber: i + 1,
444
+ type: DiffType.REMOVED,
445
+ leftContent: oldLines[i],
446
+ leftRaw: oldLines[i]
447
+ });
448
+ }
449
+ for (let k = pairs; k < inss.length; k++) {
450
+ const j = inss[k];
451
+ result.push({
452
+ rightLineNumber: j + 1,
453
+ type: DiffType.ADDED,
454
+ rightContent: newLines[j],
455
+ rightRaw: newLines[j]
456
+ });
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Turn the edit script into rows, grouping each run of unmatched lines into a
462
+ * hunk so modification pairing stays local to a real change.
463
+ */
464
+ function buildRowsFromOps(ops, oldLines, newLines, compareMethod, disableWordDiff) {
269
465
  const result = [];
270
- let leftLineNum = 1;
271
- let rightLineNum = 1;
466
+ let idx = 0;
467
+ while (idx < ops.length) {
468
+ const op = ops[idx];
469
+ if (op.kind === "eq") {
470
+ result.push({
471
+ leftLineNumber: op.i + 1,
472
+ rightLineNumber: op.j + 1,
473
+ type: DiffType.DEFAULT,
474
+ leftContent: oldLines[op.i],
475
+ rightContent: newLines[op.j],
476
+ leftRaw: oldLines[op.i],
477
+ rightRaw: newLines[op.j]
478
+ });
479
+ idx++;
480
+ continue;
481
+ }
482
+ const dels = [];
483
+ const inss = [];
484
+ while (idx < ops.length && ops[idx].kind !== "eq") {
485
+ const hunkOp = ops[idx];
486
+ if (hunkOp.kind === "del") dels.push(hunkOp.i);else if (hunkOp.kind === "ins") inss.push(hunkOp.j);
487
+ idx++;
488
+ }
489
+ emitHunk(result, dels, inss, oldLines, newLines, compareMethod, disableWordDiff);
490
+ }
491
+ return result;
492
+ }
272
493
 
273
- // Simple line diff algorithm (can be improved with LCS)
274
- let i = 0,
275
- j = 0;
494
+ /**
495
+ * Fallback pairwise walk, used only when the input is past the Myers bounds.
496
+ * Advances both sides in lockstep, so a shifted line reads as changed — the
497
+ * reason it isn't the primary path.
498
+ */
499
+ function pairwiseLineWalk(oldLines, newLines, oldKeys, newKeys, compareMethod, disableWordDiff) {
500
+ const result = [];
501
+ let i = 0;
502
+ let j = 0;
276
503
  while (i < oldLines.length || j < newLines.length) {
277
504
  if (i >= oldLines.length) {
278
- // Rest are additions
279
505
  result.push({
280
- rightLineNumber: rightLineNum++,
506
+ rightLineNumber: j + 1,
281
507
  type: DiffType.ADDED,
282
508
  rightContent: newLines[j],
283
509
  rightRaw: newLines[j]
284
510
  });
285
511
  j++;
286
512
  } else if (j >= newLines.length) {
287
- // Rest are removals
288
513
  result.push({
289
- leftLineNumber: leftLineNum++,
514
+ leftLineNumber: i + 1,
290
515
  type: DiffType.REMOVED,
291
516
  leftContent: oldLines[i],
292
517
  leftRaw: oldLines[i]
293
518
  });
294
519
  i++;
295
- } else if (compareLinesWithMethod(oldLines[i], newLines[j], compareMethod === "trimmedLines" ? "trimmedLines" : "lines")) {
296
- // Lines match (possibly after trimming if trimmedLines)
520
+ } else if (oldKeys[i] === newKeys[j]) {
297
521
  result.push({
298
- leftLineNumber: leftLineNum++,
299
- rightLineNumber: rightLineNum++,
522
+ leftLineNumber: i + 1,
523
+ rightLineNumber: j + 1,
300
524
  type: DiffType.DEFAULT,
301
525
  leftContent: oldLines[i],
302
526
  rightContent: newLines[j],
@@ -306,57 +530,35 @@ function computeLineDiff(oldValue, newValue, options = {}) {
306
530
  i++;
307
531
  j++;
308
532
  } else {
309
- // Lines differ - check if it's a modification or separate add/remove
310
- const oldTrimmed = oldLines[i].trim();
311
- const newTrimmed = newLines[j].trim();
312
-
313
- // Simple heuristic: if lines start similarly, treat as modification
314
- if (oldTrimmed && newTrimmed && (oldTrimmed.startsWith(newTrimmed.substring(0, 3)) || newTrimmed.startsWith(oldTrimmed.substring(0, 3)))) {
315
- // Treat as modification - compute word diff if enabled
316
- if (!disableWordDiff && compareMethod !== "lines") {
317
- const wordDiff = computeDiffByMethod(oldLines[i], newLines[j], compareMethod);
318
- result.push({
319
- leftLineNumber: leftLineNum++,
320
- rightLineNumber: rightLineNum++,
321
- type: DiffType.MODIFIED,
322
- leftContent: wordDiff.left,
323
- rightContent: wordDiff.right,
324
- leftRaw: oldLines[i],
325
- rightRaw: newLines[j]
326
- });
327
- } else {
328
- // No word diff - just mark lines as different
329
- result.push({
330
- leftLineNumber: leftLineNum++,
331
- rightLineNumber: rightLineNum++,
332
- type: DiffType.MODIFIED,
333
- leftContent: oldLines[i],
334
- rightContent: newLines[j],
335
- leftRaw: oldLines[i],
336
- rightRaw: newLines[j]
337
- });
338
- }
339
- i++;
340
- j++;
341
- } else {
342
- // Treat as separate remove and add
343
- result.push({
344
- leftLineNumber: leftLineNum++,
345
- type: DiffType.REMOVED,
346
- leftContent: oldLines[i],
347
- leftRaw: oldLines[i]
348
- });
349
- result.push({
350
- rightLineNumber: rightLineNum++,
351
- type: DiffType.ADDED,
352
- rightContent: newLines[j],
353
- rightRaw: newLines[j]
354
- });
355
- i++;
356
- j++;
357
- }
533
+ emitHunk(result, [i], [j], oldLines, newLines, compareMethod, disableWordDiff);
534
+ i++;
535
+ j++;
358
536
  }
359
537
  }
538
+ return result;
539
+ }
540
+
541
+ /**
542
+ * Compute line-by-line diff between two objects
543
+ *
544
+ * Lines are matched with Myers' O(ND) algorithm over normalized keys, so an
545
+ * insert or append shows up as the one line that actually arrived instead of
546
+ * every line after it shifting into a change.
547
+ */
548
+ function computeLineDiff(oldValue, newValue, options = {}) {
549
+ const {
550
+ compareMethod = "words",
551
+ disableWordDiff = false,
552
+ showDiffOnly = false,
553
+ contextLines = 3,
554
+ ignoreTrailingComma = true
555
+ } = options;
556
+ const oldLines = objectToLines(oldValue);
557
+ const newLines = objectToLines(newValue);
558
+ const oldKeys = oldLines.map(line => normalizeCompareKey(line, compareMethod, ignoreTrailingComma));
559
+ const newKeys = newLines.map(line => normalizeCompareKey(line, compareMethod, ignoreTrailingComma));
560
+ const ops = myersLineOps(oldKeys, newKeys);
561
+ const result = ops ? buildRowsFromOps(ops, oldLines, newLines, compareMethod, disableWordDiff) : pairwiseLineWalk(oldLines, newLines, oldKeys, newKeys, compareMethod, disableWordDiff);
360
562
 
361
563
  // Apply showDiffOnly filter if enabled
362
564
  if (showDiffOnly) {
@@ -7,6 +7,7 @@ exports.default = TreeDiffViewer;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _reactNative = require("react-native");
9
9
  var _sharedUi = require("@buoy-gg/shared-ui");
10
+ var _lineDiff = require("../lineDiff.js");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
13
  // @ts-nocheck
@@ -110,6 +111,11 @@ function isEqual(a, b) {
110
111
  function computeDiff(oldValue, newValue, path = []) {
111
112
  const result = [];
112
113
 
114
+ // A side marked absent never existed, so it contributes nothing and the other
115
+ // side reads as purely added (or removed). Only the root can be absent.
116
+ if ((0, _lineDiff.isAbsent)(oldValue)) oldValue = undefined;
117
+ if ((0, _lineDiff.isAbsent)(newValue)) newValue = undefined;
118
+
113
119
  // Handle primitives and null/undefined
114
120
  if (!isObject(oldValue) && !isObject(newValue) && !Array.isArray(oldValue) && !Array.isArray(newValue)) {
115
121
  if (!isEqual(oldValue, newValue)) {
@@ -322,7 +328,8 @@ function TreeDiffViewer({
322
328
  newValue,
323
329
  theme: themeName = "dark",
324
330
  expandAll = false,
325
- showUnchanged = true
331
+ showUnchanged = true,
332
+ scrollEnabled = true
326
333
  }) {
327
334
  const theme = themeName === "dark" ? darkTheme : lightTheme;
328
335
 
@@ -637,8 +644,12 @@ function TreeDiffViewer({
637
644
 
638
645
  // Show header only if there are changes
639
646
  const hasChanges = stats.added > 0 || stats.removed > 0 || stats.changed > 0;
647
+
648
+ // Inline mode must not claim `flex: 1`, or it would stretch to the pane and
649
+ // clip the rows an ancestor ScrollView is supposed to reveal.
650
+ const RowsContainer = scrollEnabled ? _reactNative.ScrollView : _reactNative.View;
640
651
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
641
- style: [styles.container, {
652
+ style: [scrollEnabled ? styles.container : styles.containerInline, {
642
653
  backgroundColor: theme.background
643
654
  }],
644
655
  children: [hasChanges && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
@@ -703,8 +714,8 @@ function TreeDiffViewer({
703
714
  })]
704
715
  })]
705
716
  })
706
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
707
- style: styles.scrollView,
717
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(RowsContainer, {
718
+ style: scrollEnabled ? styles.scrollView : undefined,
708
719
  showsVerticalScrollIndicator: false,
709
720
  children: diffTree.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
710
721
  style: styles.emptyState,
@@ -744,6 +755,10 @@ const styles = _reactNative.StyleSheet.create({
744
755
  borderRadius: 8,
745
756
  overflow: "hidden"
746
757
  },
758
+ containerInline: {
759
+ borderRadius: 8,
760
+ overflow: "hidden"
761
+ },
747
762
  header: {
748
763
  paddingHorizontal: 12,
749
764
  paddingVertical: 8,
@@ -795,7 +810,7 @@ const styles = _reactNative.StyleSheet.create({
795
810
  justifyContent: "center",
796
811
  flexDirection: "row",
797
812
  borderBottomWidth: _reactNative.StyleSheet.hairlineWidth,
798
- borderBottomColor: "rgba(255, 255, 255, 0.02)"
813
+ borderBottomColor: "rgba(128, 128, 128, 0.04)"
799
814
  },
800
815
  lineNumber: {
801
816
  width: 32,
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.breadcrumb = breadcrumb;
7
+ exports.treeActionMap = treeActionMap;
8
+ exports.treeActionsFor = treeActionsFor;
9
+ exports.typeLabel = typeLabel;
10
+ var _jsonOps = require("./jsonOps.js");
11
+ /**
12
+ * What you can do to the node a data explorer has selected — as data, not UI.
13
+ *
14
+ * Two surfaces render this list and they look nothing alike: the dashboard's
15
+ * footer bar is a row of small chips beside a cursor, the phone's full-screen
16
+ * editor is a grid of thumb-sized tiles. Deriving both from one function is the
17
+ * point — "can this item move up" is a property of the DOCUMENT, and having two
18
+ * components each answer it from their own reading of `root` is how the two
19
+ * surfaces quietly start disagreeing about what's legal.
20
+ *
21
+ * So this module answers it once and hands back a list. The renderers decide
22
+ * what a disabled action looks like and which icon it wears; they never decide
23
+ * whether it applies.
24
+ */
25
+
26
+ /** The node a data explorer currently has selected. */
27
+
28
+ /** The node's type, as a badge shows it. */
29
+ function typeLabel(value) {
30
+ if (value === null) return "null";
31
+ if (Array.isArray(value)) return "array";
32
+ if ((0, _jsonOps.isRebuildableObject)(value)) return "object";
33
+ return typeof value;
34
+ }
35
+
36
+ /** `a › b › 2` — reads as a location, which a bare index like `2` does not. */
37
+ function breadcrumb(path) {
38
+ return path.length === 0 ? "root" : path.join(" › ");
39
+ }
40
+
41
+ /**
42
+ * Index the list by id, for renderers that lay the six out in their own order.
43
+ *
44
+ * The bar and the dock genuinely disagree here — the bar puts the two move
45
+ * arrows together in the middle because they're icon-only and read as a pair,
46
+ * the dock follows the mockup's reading order. Neither ordering belongs in the
47
+ * capability list, so both look up what they need.
48
+ */
49
+ function treeActionMap(actions) {
50
+ const map = {};
51
+ for (const action of actions) map[action.id] = action;
52
+ return map;
53
+ }
54
+
55
+ /** A key that doesn't collide, for "add" on an object. */
56
+ function newKeyFor(container) {
57
+ if (!("newKey" in container)) return "newKey";
58
+ let n = 2;
59
+ while (`newKey ${n}` in container) n++;
60
+ return `newKey ${n}`;
61
+ }
62
+
63
+ /**
64
+ * The six things a selection can do, ALWAYS in this order and always all six.
65
+ *
66
+ * Returning the inapplicable ones as `enabled: false` rather than omitting them
67
+ * is deliberate and is why this returns a fixed-length list: a renderer that
68
+ * drops entries reflows every time the selection moves, so stepping from item 1
69
+ * to item 0 would slide the remaining controls sideways under a finger already
70
+ * on its way down. Stable position also means the same action is always in the
71
+ * same place, which is most of what makes a grid of six learnable.
72
+ */
73
+ function treeActionsFor(root, selection, options = {}) {
74
+ const path = selection?.path ?? [];
75
+ const value = selection?.value;
76
+ const has = selection != null;
77
+ const isRoot = path.length === 0;
78
+ const parent = has && !isRoot ? (0, _jsonOps.getAtPath)(root, path.slice(0, -1)) : undefined;
79
+ const parentIsArray = Array.isArray(parent);
80
+ const index = parentIsArray ? Number(path[path.length - 1]) : -1;
81
+ const isArray = Array.isArray(value);
82
+ const isObject = (0, _jsonOps.isRebuildableObject)(value);
83
+ const isContainer = (0, _jsonOps.isEditableContainer)(value);
84
+ const canEditValue = has && (options.containersEditable === true || !isContainer);
85
+
86
+ // One "+" slot whose meaning follows the container type, rather than two
87
+ // mutually-exclusive buttons of which one is always dead.
88
+ const addedKey = isObject ? newKeyFor(value) : "";
89
+ const add = isObject ? {
90
+ id: "add",
91
+ label: "Add key",
92
+ enabled: true,
93
+ op: {
94
+ kind: "addKey",
95
+ path,
96
+ key: addedKey,
97
+ value: (0, _jsonOps.suggestedNewValue)(value)
98
+ },
99
+ selectAfter: [...path, addedKey]
100
+ } : {
101
+ id: "add",
102
+ label: "Append",
103
+ enabled: isArray,
104
+ op: isArray ? {
105
+ kind: "append",
106
+ path,
107
+ value: (0, _jsonOps.suggestedNewValue)(value)
108
+ } : null,
109
+ selectAfter: isArray ? [...path, String(value.length)] : undefined
110
+ };
111
+ return [
112
+ // A boolean has exactly two states, so there is nothing to ask and nothing
113
+ // to type — an editor for it would be a screen whose only job is to offer
114
+ // the value you didn't pick. It flips in place instead.
115
+ typeof value === "boolean" ? {
116
+ id: "editValue",
117
+ label: "Toggle",
118
+ enabled: true,
119
+ op: {
120
+ kind: "replace",
121
+ path,
122
+ value: !value
123
+ }
124
+ } : {
125
+ id: "editValue",
126
+ label: "Edit value",
127
+ enabled: canEditValue,
128
+ op: null
129
+ }, add, {
130
+ // `duplicate` handles object keys too (it mints a "<key> copy" in place),
131
+ // so this applies to any entry that has a parent.
132
+ id: "duplicate",
133
+ label: "Duplicate",
134
+ enabled: has && !isRoot,
135
+ op: has && !isRoot ? {
136
+ kind: "duplicate",
137
+ path
138
+ } : null
139
+ }, {
140
+ id: "moveUp",
141
+ label: "Move up",
142
+ enabled: has && parentIsArray && index > 0,
143
+ op: has && parentIsArray && index > 0 ? {
144
+ kind: "move",
145
+ path,
146
+ toIndex: index - 1
147
+ } : null
148
+ }, {
149
+ id: "moveDown",
150
+ label: "Move down",
151
+ enabled: has && parentIsArray && index > -1 && index < parent.length - 1,
152
+ op: has && parentIsArray && index > -1 && index < parent.length - 1 ? {
153
+ kind: "move",
154
+ path,
155
+ toIndex: index + 1
156
+ } : null
157
+ }, {
158
+ id: "remove",
159
+ label: "Remove",
160
+ danger: true,
161
+ enabled: has && !isRoot,
162
+ op: has && !isRoot ? {
163
+ kind: "remove",
164
+ path
165
+ } : null
166
+ }];
167
+ }
@@ -180,10 +180,10 @@ const styles = _reactNative.StyleSheet.create({
180
180
  width: "100%"
181
181
  },
182
182
  optionContainerSelected: {
183
- backgroundColor: "rgba(255, 255, 255, 0.05)"
183
+ backgroundColor: "rgba(128, 128, 128, 0.1)"
184
184
  },
185
185
  optionContainerPressed: {
186
- backgroundColor: "rgba(255, 255, 255, 0.1)"
186
+ backgroundColor: "rgba(128, 128, 128, 0.2)"
187
187
  },
188
188
  optionLeft: {
189
189
  flexDirection: "row",
@@ -274,10 +274,10 @@ const styles = _reactNative.StyleSheet.create({
274
274
  height: OPTION_HEIGHT
275
275
  },
276
276
  optionContainerSelected: {
277
- backgroundColor: "rgba(255, 255, 255, 0.05)"
277
+ backgroundColor: "rgba(128, 128, 128, 0.1)"
278
278
  },
279
279
  optionContainerPressed: {
280
- backgroundColor: "rgba(255, 255, 255, 0.1)"
280
+ backgroundColor: "rgba(128, 128, 128, 0.2)"
281
281
  },
282
282
  optionLeft: {
283
283
  flexDirection: "row",