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