@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
@@ -25,6 +25,8 @@ const CHUNK_SIZE = 50; // Process data in chunks to avoid blocking UI
25
25
  const MAX_DEPTH_LIMIT = 15; // Prevent excessive nesting
26
26
  const MAX_ITEMS_PER_LEVEL = 500; // Limit items to prevent memory issues
27
27
  const LONG_KEY_THRESHOLD = 30; // Keys longer than this use vertical layout
28
+ /** Two presses inside this window count as a double-tap (RN has no dblclick). */
29
+ const DOUBLE_TAP_MS = 300;
28
30
 
29
31
  // Pre-computed indent styles (VS Code-style width)
30
32
  const INDENT_WIDTH = 16;
@@ -223,8 +225,48 @@ const STABLE_STYLES = _reactNative.StyleSheet.create({
223
225
  }
224
226
  });
225
227
 
228
+ /**
229
+ * A tree row as the HOST sees it.
230
+ *
231
+ * `item.path` is addressed for the TREE, which is seeded with a synthetic
232
+ * "root" node wrapping the caller's data — so the top row is `["root"]` and its
233
+ * first child `["root", "0"]`. Ops address the DATA, where those are `[]` and
234
+ * `["0"]`. Dropping the first segment is what reconciles the two, and doing it
235
+ * in ONE place is what keeps selection, inline editing and ops all agreeing.
236
+ */
237
+ function selectionOf(item) {
238
+ return {
239
+ path: item.path.slice(1),
240
+ value: item.value,
241
+ label: item.key
242
+ };
243
+ }
244
+
226
245
  // Type definitions for flattened data structure
227
246
 
247
+ /**
248
+ * Node count, capped at `limit`.
249
+ *
250
+ * Deliberately short-circuits: this runs on every render to decide which
251
+ * flattening path to take, so it must cost O(limit), never O(document).
252
+ */
253
+ function countNodesUpTo(value, limit, seen = 0) {
254
+ if (seen >= limit) return seen;
255
+ let n = seen + 1;
256
+ if (Array.isArray(value)) {
257
+ for (const child of value) {
258
+ if (n >= limit) return n;
259
+ n = countNodesUpTo(child, limit, n);
260
+ }
261
+ } else if (value !== null && typeof value === "object") {
262
+ for (const child of Object.values(value)) {
263
+ if (n >= limit) return n;
264
+ n = countNodesUpTo(child, limit, n);
265
+ }
266
+ }
267
+ return n;
268
+ }
269
+
228
270
  // Enhanced type detection optimized for performance
229
271
  const getValueType = value => {
230
272
  if (value === null) return "null";
@@ -376,6 +418,16 @@ const useDataFlattening = (data, maxDepth = 10, autoExpandFirstLevel = false) =>
376
418
  // Store circular cache outside of re-renders to prevent reset
377
419
  const circularCacheRef = (0, _react.useRef)(new WeakSet());
378
420
  const processingRef = (0, _react.useRef)(false);
421
+ /**
422
+ * Monotonic token identifying the newest flatten run.
423
+ *
424
+ * WHY: a cancelled run used to `return` without clearing `isProcessing`, so
425
+ * whenever renders arrived faster than the 10ms debounce + flatten, every run
426
+ * was superseded before it finished and the spinner never came down — the
427
+ * viewer sat on "Processing data…" forever. Now each run checks it is still
428
+ * the current one, and the run that ends up last ALWAYS resolves the flag.
429
+ */
430
+ const runIdRef = (0, _react.useRef)(0);
379
431
  const dataVersionRef = (0, _react.useRef)(0);
380
432
  const lastActionRef = (0, _react.useRef)(undefined);
381
433
 
@@ -505,14 +557,16 @@ const useDataFlattening = (data, maxDepth = 10, autoExpandFirstLevel = false) =>
505
557
  if (processingRef.current) {
506
558
  return;
507
559
  }
508
- let isCancelled = false;
509
560
  let timeoutId;
561
+ const runId = ++runIdRef.current;
562
+ /** True while this run is still the newest one scheduled. */
563
+ const isCurrent = () => runIdRef.current === runId;
510
564
  processingRef.current = true;
511
565
  setIsProcessing(true);
512
566
  const processData = async () => {
513
567
  // Failsafe timeout to prevent stuck processing
514
568
  timeoutId = setTimeout(() => {
515
- if (processingRef.current && !isCancelled) {
569
+ if (processingRef.current && isCurrent()) {
516
570
  setIsProcessing(false);
517
571
  processingRef.current = false;
518
572
  }
@@ -520,8 +574,10 @@ const useDataFlattening = (data, maxDepth = 10, autoExpandFirstLevel = false) =>
520
574
  // Small delay to debounce rapid changes
521
575
  // Small delay to batch rapid changes
522
576
  await new Promise(resolve => setTimeout(() => resolve(), 10));
523
- if (isCancelled) {
524
- processingRef.current = false;
577
+
578
+ // Superseded by a newer run — that run now owns `isProcessing`, so leave
579
+ // the flag alone and let it resolve.
580
+ if (!isCurrent()) {
525
581
  return;
526
582
  }
527
583
  try {
@@ -539,31 +595,26 @@ const useDataFlattening = (data, maxDepth = 10, autoExpandFirstLevel = false) =>
539
595
  });
540
596
  });
541
597
  flatDataMapRef.current = newMap;
542
- if (!isCancelled) {
598
+ if (timeoutId) clearTimeout(timeoutId);
599
+ if (isCurrent()) {
543
600
  setFlatData(newFlatData);
544
601
  setIsProcessing(false);
545
602
  processingRef.current = false;
546
- if (timeoutId) clearTimeout(timeoutId);
547
- } else {
548
- if (timeoutId) clearTimeout(timeoutId);
549
603
  }
550
604
  } catch (error) {
551
605
  console.error(error);
552
606
  // Reset to empty data on error
553
- if (!isCancelled) {
607
+ if (timeoutId) clearTimeout(timeoutId);
608
+ if (isCurrent()) {
554
609
  setFlatData([]);
555
610
  flatDataMapRef.current = new Map();
556
611
  setIsProcessing(false);
557
612
  processingRef.current = false;
558
- if (timeoutId) clearTimeout(timeoutId);
559
- } else {
560
- if (timeoutId) clearTimeout(timeoutId);
561
613
  }
562
614
  }
563
615
  };
564
616
  processData();
565
617
  return () => {
566
- isCancelled = true;
567
618
  processingRef.current = false;
568
619
  if (timeoutId) clearTimeout(timeoutId);
569
620
  };
@@ -715,10 +766,50 @@ const useDataFlattening = (data, maxDepth = 10, autoExpandFirstLevel = false) =>
715
766
  return newSet;
716
767
  });
717
768
  }, [updateFlatDataIncremental]);
769
+
770
+ /**
771
+ * Small documents flatten SYNCHRONOUSLY, during render.
772
+ *
773
+ * The chunked effect above exists so a 500-node payload can't block a frame,
774
+ * and it pays for that with an async, multi-commit rebuild every time `data`
775
+ * changes identity. Editing is immutable, so that's every single op — which
776
+ * made a three-item array rebuild itself in stages on every tap, visibly, for
777
+ * machinery it never needed. Below the chunk threshold the entire flatten is
778
+ * cheaper than one of those commits, so derive it inline and let React
779
+ * reconcile the rows normally. The chunked path is untouched above that size.
780
+ */
781
+ const isSmallDocument = countNodesUpTo(data, CHUNK_SIZE, 0) < CHUNK_SIZE;
782
+ const syncFlatData = (0, _react.useMemo)(() => {
783
+ if (!isSmallDocument) return null;
784
+ return flattenDataStable(data, expandedItems, new WeakSet(), "root", 0, undefined, [], 0, 1, []);
785
+ }, [isSmallDocument, data, expandedItems, flattenDataStable]);
786
+
787
+ /**
788
+ * Expand a set of ancestor ids so a node deeper in the tree becomes visible.
789
+ *
790
+ * Needed because a host can relocate the selection to a node the user never
791
+ * expanded their way to — append into a collapsed array and the new entry is
792
+ * selected but off-tree, which reads as "nothing happened". Unlike
793
+ * `toggleExpanded` this doesn't patch `flatData` incrementally; the full
794
+ * flatten effect already re-runs on `expandedItems`, and a reveal is rare
795
+ * enough that the extra pass isn't worth a second incremental code path.
796
+ */
797
+ const revealPath = (0, _react.useCallback)(ancestorIds => {
798
+ if (ancestorIds.length === 0) return;
799
+ setExpandedItems(prev => {
800
+ if (ancestorIds.every(id => prev.has(id))) return prev;
801
+ const next = new Set(prev);
802
+ for (const id of ancestorIds) next.add(id);
803
+ return next;
804
+ });
805
+ }, []);
718
806
  return {
719
- flatData,
720
- isProcessing,
721
- toggleExpanded
807
+ flatData: syncFlatData ?? flatData,
808
+ // A synchronous list is never "processing" — the spinner would flash on
809
+ // every keystroke otherwise.
810
+ isProcessing: syncFlatData ? false : isProcessing,
811
+ toggleExpanded,
812
+ revealPath
722
813
  };
723
814
  };
724
815
 
@@ -728,10 +819,28 @@ const VirtualizedItemComponent = ({
728
819
  onToggleExpanded,
729
820
  index,
730
821
  onSelect,
731
- isSelected
822
+ isSelected,
823
+ selectable,
824
+ onRequestInlineEdit,
825
+ isInlineEditing,
826
+ inlineEditor
732
827
  }) => {
733
828
  const [isPressed, setIsPressed] = (0, _react.useState)(false);
734
829
  const [showFullKey, setShowFullKey] = (0, _react.useState)(false);
830
+ const canEdit = selectable === true;
831
+ const lastPressRef = (0, _react.useRef)(0);
832
+
833
+ /**
834
+ * `item.path` is addressed for the TREE, which is seeded with a synthetic
835
+ * "root" node wrapping the caller's data — so the top row is `["root"]` and
836
+ * its first child `["root", "0"]`. Ops address the DATA, where those are `[]`
837
+ * and `["0"]`. Dropping the first segment is what reconciles the two.
838
+ *
839
+ * Worth stating plainly because getting it wrong fails silently: `applyJsonOp`
840
+ * treats an unresolvable path as a no-op, so every button would simply have
841
+ * done nothing rather than erroring.
842
+ */
843
+ const dataPath = item.path.slice(1);
735
844
 
736
845
  // Use pre-computed styles to avoid inline calculations [[memory:4875251]]
737
846
  const indentStyle = INDENT_STYLES[Math.min(item.depth, MAX_DEPTH_LIMIT)] || INDENT_STYLES[0];
@@ -741,11 +850,40 @@ const VirtualizedItemComponent = ({
741
850
  const isLongKey = false;
742
851
 
743
852
  // Use inline handler since component is already memoized [[memory:4875251]]
853
+
854
+ /**
855
+ * Row body. In editable mode this SELECTS only — pressing a container to see
856
+ * its actions would otherwise collapse the very subtree you're editing, and
857
+ * the expander already exists to do the toggling (the same split every tree
858
+ * view makes). Read-only mode keeps the original toggle-on-row-press.
859
+ */
744
860
  const handlePress = () => {
861
+ if (canEdit) {
862
+ // Double-tap opens the inline editor, matching the mockup's
863
+ // double-click. Detected by hand because RN has no dblclick: two presses
864
+ // inside DOUBLE_TAP_MS on an already-selected scalar row.
865
+ const now = Date.now();
866
+ const isDouble = now - lastPressRef.current < DOUBLE_TAP_MS;
867
+ lastPressRef.current = now;
868
+ if (isDouble && !item.isExpandable && onRequestInlineEdit) {
869
+ onRequestInlineEdit(item);
870
+ return;
871
+ }
872
+ onSelect(item);
873
+ return;
874
+ }
875
+ if (item.isExpandable) {
876
+ onToggleExpanded(item.id);
877
+ }
878
+ onSelect(item);
879
+ };
880
+
881
+ /** The chevron always toggles, in both modes. */
882
+ const handleExpanderPress = () => {
745
883
  if (item.isExpandable) {
746
884
  onToggleExpanded(item.id);
747
885
  }
748
- onSelect(index);
886
+ onSelect(item);
749
887
  };
750
888
  const handleKeyPress = () => {};
751
889
 
@@ -762,11 +900,16 @@ const VirtualizedItemComponent = ({
762
900
  onPress: handlePress,
763
901
  onPressIn: () => setIsPressed(true),
764
902
  onPressOut: () => setIsPressed(false),
765
- activeOpacity: item.isExpandable ? 0.7 : 1,
766
- disabled: !item.isExpandable,
903
+ activeOpacity: item.isExpandable || canEdit ? 0.7 : 1
904
+ // A leaf row has nothing to expand, so it was left unpressable. In
905
+ // editable mode the press IS the selection that reveals this row's
906
+ // actions, so scalars — the ones you most want to edit — have to be
907
+ // pressable too.
908
+ ,
909
+ disabled: !item.isExpandable && !canEdit,
767
910
  children: [item.isExpandable ? /*#__PURE__*/(0, _jsxRuntime.jsx)(Expander, {
768
911
  expanded: item.isExpanded,
769
- onPress: handlePress
912
+ onPress: handleExpanderPress
770
913
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
771
914
  style: STABLE_STYLES.expanderContainer
772
915
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
@@ -775,7 +918,7 @@ const VirtualizedItemComponent = ({
775
918
  style: STABLE_STYLES.labelText,
776
919
  numberOfLines: 1,
777
920
  children: [item.key, ":"]
778
- }), item.isExpandable ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
921
+ }), isInlineEditing && inlineEditor ? inlineEditor : item.isExpandable ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
779
922
  style: [STABLE_STYLES.valueText, {
780
923
  color: _index.gameUIColors.secondary
781
924
  }],
@@ -812,13 +955,19 @@ const VirtualizedDataExplorer = ({
812
955
  rawMode = false,
813
956
  initialExpanded = false,
814
957
  // disableCopy is accepted for API compatibility but CopyButton handles Pro gating internally
815
- disableCopy: _disableCopy = false
958
+ disableCopy: _disableCopy = false,
959
+ onSelectionChange,
960
+ onRequestInlineEdit,
961
+ inlineEditPath,
962
+ renderInlineEditor,
963
+ selectPath
816
964
  }) => {
817
965
  const [isExpanded, setIsExpanded] = (0, _react.useState)(rawMode); // Auto-expand in raw mode
818
966
  const {
819
967
  flatData,
820
968
  isProcessing,
821
- toggleExpanded
969
+ toggleExpanded,
970
+ revealPath
822
971
  } = useDataFlattening(data, maxDepth, initialExpanded);
823
972
 
824
973
  // Track visible range for overlay rendering
@@ -867,8 +1016,54 @@ const VirtualizedDataExplorer = ({
867
1016
  };
868
1017
 
869
1018
  // Stable renderItem using module-scope function [[memory:4875251]]
870
- const [selectedIndex, setSelectedIndex] = (0, _react.useState)(null);
871
- const activeDepth = selectedIndex != null ? flatData[selectedIndex]?.depth : undefined;
1019
+ /**
1020
+ * Selection is tracked by the row's stable id (its path), NOT its index.
1021
+ *
1022
+ * An edit reshapes the list — removing item 1 slides everything up — so an
1023
+ * index-based selection would silently start pointing at a different node
1024
+ * and the action bar would operate on the wrong one. An id survives that, and
1025
+ * resolves to nothing when the node it named is gone.
1026
+ */
1027
+ const [selectedId, setSelectedId] = (0, _react.useState)(null);
1028
+ const selectedItem = (0, _react.useMemo)(() => selectedId == null ? undefined : flatData.find(it => it.id === selectedId), [flatData, selectedId]);
1029
+ const activeDepth = selectedItem?.depth;
1030
+
1031
+ // Report the selection outward, re-emitting after each data change so the
1032
+ // action bar is looking at the CURRENT value of the node it has selected.
1033
+ (0, _react.useEffect)(() => {
1034
+ if (!onSelectionChange) return;
1035
+ onSelectionChange(selectedItem ? selectionOf(selectedItem) : null);
1036
+ }, [selectedItem, onSelectionChange]);
1037
+
1038
+ /** Tapping the selected row again clears it, so the bar can be dismissed. */
1039
+ const handleSelect = (0, _react.useCallback)(item => {
1040
+ setSelectedId(prev => prev === item.id ? null : item.id);
1041
+ }, []);
1042
+
1043
+ // Re-sync when the host relocates the selection. Keyed on the joined path so
1044
+ // an unchanged array identity (a fresh render) doesn't clobber a user's tap.
1045
+ const selectPathKey = selectPath === undefined ? undefined : selectPath === null ? "\u0000null" : selectPath.join(".");
1046
+ const lastSelectPathKey = (0, _react.useRef)(selectPathKey);
1047
+ (0, _react.useEffect)(() => {
1048
+ if (selectPathKey === lastSelectPathKey.current) return;
1049
+ lastSelectPathKey.current = selectPathKey;
1050
+ if (selectPathKey === undefined) return;
1051
+ if (selectPath == null) {
1052
+ setSelectedId(null);
1053
+ return;
1054
+ }
1055
+ // Reveal it first — a selection the user can't see reads as a no-op.
1056
+ const ancestors = ["root"];
1057
+ for (const segment of selectPath.slice(0, -1)) {
1058
+ ancestors.push(`${ancestors[ancestors.length - 1]}.${segment}`);
1059
+ }
1060
+ revealPath(ancestors);
1061
+ setSelectedId(["root", ...selectPath].join("."));
1062
+ }, [selectPathKey, selectPath, revealPath]);
1063
+ const handleRequestInlineEdit = (0, _react.useCallback)(item => {
1064
+ setSelectedId(item.id);
1065
+ onRequestInlineEdit?.(selectionOf(item));
1066
+ }, [onRequestInlineEdit]);
872
1067
  const renderItem = ({
873
1068
  item,
874
1069
  index
@@ -876,8 +1071,12 @@ const VirtualizedDataExplorer = ({
876
1071
  item: item,
877
1072
  index: index,
878
1073
  onToggleExpanded: toggleExpanded,
879
- onSelect: setSelectedIndex,
880
- isSelected: selectedIndex === index
1074
+ onSelect: handleSelect,
1075
+ isSelected: selectedItem?.id === item.id,
1076
+ selectable: onSelectionChange != null,
1077
+ onRequestInlineEdit: handleRequestInlineEdit,
1078
+ isInlineEditing: inlineEditPath != null && inlineEditPath.join(".") === item.path.slice(1).join("."),
1079
+ inlineEditor: renderInlineEditor ? renderInlineEditor(selectionOf(item)) : null
881
1080
  });
882
1081
 
883
1082
  // Uniform row height for crisp guide geometry
@@ -3,12 +3,36 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "ABSENT_VALUE", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _lineDiff.ABSENT_VALUE;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "CyberpunkInput", {
7
13
  enumerable: true,
8
14
  get: function () {
9
15
  return _CyberpunkInput.CyberpunkInput;
10
16
  }
11
17
  });
18
+ Object.defineProperty(exports, "DataTreeActionBar", {
19
+ enumerable: true,
20
+ get: function () {
21
+ return _DataTreeActionBar.DataTreeActionBar;
22
+ }
23
+ });
24
+ Object.defineProperty(exports, "DataTreeActionDock", {
25
+ enumerable: true,
26
+ get: function () {
27
+ return _DataTreeActionDock.DataTreeActionDock;
28
+ }
29
+ });
30
+ Object.defineProperty(exports, "DataTreeInlineEditor", {
31
+ enumerable: true,
32
+ get: function () {
33
+ return _DataTreeActionBar.DataTreeInlineEditor;
34
+ }
35
+ });
12
36
  Object.defineProperty(exports, "DataViewer", {
13
37
  enumerable: true,
14
38
  get: function () {
@@ -63,6 +87,18 @@ Object.defineProperty(exports, "VirtualizedDataExplorer", {
63
87
  return _VirtualizedDataExplorer.VirtualizedDataExplorer;
64
88
  }
65
89
  });
90
+ Object.defineProperty(exports, "applyJsonOp", {
91
+ enumerable: true,
92
+ get: function () {
93
+ return _jsonOps.applyJsonOp;
94
+ }
95
+ });
96
+ Object.defineProperty(exports, "breadcrumb", {
97
+ enumerable: true,
98
+ get: function () {
99
+ return _treeActions.breadcrumb;
100
+ }
101
+ });
66
102
  Object.defineProperty(exports, "computeLineDiff", {
67
103
  enumerable: true,
68
104
  get: function () {
@@ -81,12 +117,60 @@ Object.defineProperty(exports, "diffThemes", {
81
117
  return _diffThemes.diffThemes;
82
118
  }
83
119
  });
120
+ Object.defineProperty(exports, "getAtPath", {
121
+ enumerable: true,
122
+ get: function () {
123
+ return _jsonOps.getAtPath;
124
+ }
125
+ });
84
126
  Object.defineProperty(exports, "gitClassicTheme", {
85
127
  enumerable: true,
86
128
  get: function () {
87
129
  return _diffThemes.gitClassicTheme;
88
130
  }
89
131
  });
132
+ Object.defineProperty(exports, "isAbsent", {
133
+ enumerable: true,
134
+ get: function () {
135
+ return _lineDiff.isAbsent;
136
+ }
137
+ });
138
+ Object.defineProperty(exports, "isEditableContainer", {
139
+ enumerable: true,
140
+ get: function () {
141
+ return _jsonOps.isEditableContainer;
142
+ }
143
+ });
144
+ Object.defineProperty(exports, "selectionPathAfterOp", {
145
+ enumerable: true,
146
+ get: function () {
147
+ return _jsonOps.selectionPathAfterOp;
148
+ }
149
+ });
150
+ Object.defineProperty(exports, "suggestedNewValue", {
151
+ enumerable: true,
152
+ get: function () {
153
+ return _jsonOps.suggestedNewValue;
154
+ }
155
+ });
156
+ Object.defineProperty(exports, "treeActionMap", {
157
+ enumerable: true,
158
+ get: function () {
159
+ return _treeActions.treeActionMap;
160
+ }
161
+ });
162
+ Object.defineProperty(exports, "treeActionsFor", {
163
+ enumerable: true,
164
+ get: function () {
165
+ return _treeActions.treeActionsFor;
166
+ }
167
+ });
168
+ Object.defineProperty(exports, "typeLabel", {
169
+ enumerable: true,
170
+ get: function () {
171
+ return _treeActions.typeLabel;
172
+ }
173
+ });
90
174
  var _DataViewer = require("./DataViewer.js");
91
175
  var _VirtualizedDataExplorer = require("./VirtualizedDataExplorer.js");
92
176
  var _TypeLegend = require("./TypeLegend.js");
@@ -94,6 +178,10 @@ var _CyberpunkInput = require("./CyberpunkInput.js");
94
178
  var _IndentGuides = require("./IndentGuides.js");
95
179
  var _IndentGuidesOverlay = require("./IndentGuidesOverlay.js");
96
180
  var _TreeDiffViewer = _interopRequireDefault(require("./tree/TreeDiffViewer.js"));
181
+ var _jsonOps = require("./jsonOps.js");
182
+ var _treeActions = require("./treeActions.js");
183
+ var _DataTreeActionBar = require("./DataTreeActionBar.js");
184
+ var _DataTreeActionDock = require("./DataTreeActionDock.js");
97
185
  var _SplitDiffViewer = require("./SplitDiffViewer.js");
98
186
  var _DiffSummary = require("./DiffSummary.js");
99
187
  var _lineDiff = require("./lineDiff.js");