@buoy-gg/storage 7.0.41 → 7.0.43
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/README.md +11 -90
- package/lib/commonjs/index.js +1 -353
- package/lib/commonjs/preset.js +1 -85
- package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +1 -36
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -12
- package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +1 -24
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +3 -1259
- package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +1 -257
- package/lib/commonjs/storage/components/MMKVInstanceSelector.js +1 -419
- package/lib/commonjs/storage/components/SelectionActionBar.js +1 -207
- package/lib/commonjs/storage/components/StorageActionButtons.js +1 -242
- package/lib/commonjs/storage/components/StorageBrowserMode.js +1 -61
- package/lib/commonjs/storage/components/StorageEventActionButton.js +1 -121
- package/lib/commonjs/storage/components/StorageEventCard.js +1 -118
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +1 -494
- package/lib/commonjs/storage/components/StorageEventFilterView.js +1 -160
- package/lib/commonjs/storage/components/StorageEventsSection.js +1 -24
- package/lib/commonjs/storage/components/StorageFilterCards.js +1 -231
- package/lib/commonjs/storage/components/StorageKeyCard.js +1 -520
- package/lib/commonjs/storage/components/StorageKeyDetailScreen.js +1 -579
- package/lib/commonjs/storage/components/StorageKeyRow.js +1 -559
- package/lib/commonjs/storage/components/StorageKeySection.js +1 -160
- package/lib/commonjs/storage/components/StorageKeyStats.js +1 -345
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +1 -954
- package/lib/commonjs/storage/components/StorageSaveNotice.js +1 -171
- package/lib/commonjs/storage/components/StorageSection.js +1 -43
- package/lib/commonjs/storage/components/StorageTypeFilterBar.js +1 -144
- package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +1 -141
- package/lib/commonjs/storage/hooks/useMMKVInstances.js +1 -210
- package/lib/commonjs/storage/hooks/useMMKVKeys.js +1 -376
- package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +1 -202
- package/lib/commonjs/storage/hooks/useStorageEvents.js +1 -61
- package/lib/commonjs/storage/stores/storageEventStore.js +1 -312
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +1 -492
- package/lib/commonjs/storage/types.js +1 -5
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +1 -691
- package/lib/commonjs/storage/utils/AsyncStorageListener.web.js +1 -89
- package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +1 -203
- package/lib/commonjs/storage/utils/MMKVListener.js +1 -416
- package/lib/commonjs/storage/utils/SecureStoreRegistry.js +1 -200
- package/lib/commonjs/storage/utils/asyncStorageCompat.js +1 -89
- package/lib/commonjs/storage/utils/browserStorageLifecycle.js +1 -10
- package/lib/commonjs/storage/utils/browserStorageLifecycle.web.js +1 -70
- package/lib/commonjs/storage/utils/clearAllStorage.js +1 -48
- package/lib/commonjs/storage/utils/deleteStorageKeys.js +1 -82
- package/lib/commonjs/storage/utils/lineDiff.js +1 -30
- package/lib/commonjs/storage/utils/mmkvAvailability.js +1 -62
- package/lib/commonjs/storage/utils/mmkvAvailability.web.js +1 -21
- package/lib/commonjs/storage/utils/mmkvTypeDetection.js +1 -154
- package/lib/commonjs/storage/utils/requiredStorageKeyScope.js +1 -17
- package/lib/commonjs/storage/utils/setStorageValue.js +1 -218
- package/lib/commonjs/storage/utils/snapshotProvider.js +1 -382
- package/lib/commonjs/storage/utils/storageActionHelpers.js +1 -46
- package/lib/commonjs/storage/utils/storageBrowserRefresh.js +1 -49
- package/lib/commonjs/storage/utils/storageLabels.js +1 -13
- package/lib/commonjs/storage/utils/storageLabels.web.js +1 -13
- package/lib/commonjs/storage/utils/storageOps.js +1 -53
- package/lib/commonjs/storage/utils/storageQueryUtils.js +1 -35
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +1 -288
- package/lib/commonjs/storage/utils/valueType.js +1 -83
- package/lib/module/index.js +1 -114
- package/lib/module/preset.js +1 -82
- package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +1 -32
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -9
- package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +1 -10
- package/lib/module/storage/components/GameUIStorageBrowser.js +3 -1257
- package/lib/module/storage/components/MMKVInstanceInfoPanel.js +1 -253
- package/lib/module/storage/components/MMKVInstanceSelector.js +1 -415
- package/lib/module/storage/components/SelectionActionBar.js +1 -204
- package/lib/module/storage/components/StorageActionButtons.js +1 -238
- package/lib/module/storage/components/StorageBrowserMode.js +1 -57
- package/lib/module/storage/components/StorageEventActionButton.js +1 -118
- package/lib/module/storage/components/StorageEventCard.js +1 -113
- package/lib/module/storage/components/StorageEventDetailContent.js +1 -490
- package/lib/module/storage/components/StorageEventFilterView.js +1 -156
- package/lib/module/storage/components/StorageEventsSection.js +1 -20
- package/lib/module/storage/components/StorageFilterCards.js +1 -227
- package/lib/module/storage/components/StorageKeyCard.js +1 -517
- package/lib/module/storage/components/StorageKeyDetailScreen.js +1 -577
- package/lib/module/storage/components/StorageKeyRow.js +1 -554
- package/lib/module/storage/components/StorageKeySection.js +1 -155
- package/lib/module/storage/components/StorageKeyStats.js +1 -342
- package/lib/module/storage/components/StorageModalWithTabs.js +1 -949
- package/lib/module/storage/components/StorageSaveNotice.js +1 -168
- package/lib/module/storage/components/StorageSection.js +1 -40
- package/lib/module/storage/components/StorageTypeFilterBar.js +1 -139
- package/lib/module/storage/hooks/useAsyncStorageKeys.js +1 -136
- package/lib/module/storage/hooks/useMMKVInstances.js +1 -204
- package/lib/module/storage/hooks/useMMKVKeys.js +1 -371
- package/lib/module/storage/hooks/useSecureStoreKeys.js +1 -199
- package/lib/module/storage/hooks/useStorageEvents.js +1 -58
- package/lib/module/storage/stores/storageEventStore.js +1 -301
- package/lib/module/storage/sync/storageSyncAdapter.js +1 -486
- package/lib/module/storage/types.js +1 -3
- package/lib/module/storage/utils/AsyncStorageListener.js +1 -679
- package/lib/module/storage/utils/AsyncStorageListener.web.js +1 -76
- package/lib/module/storage/utils/MMKVInstanceRegistry.js +1 -197
- package/lib/module/storage/utils/MMKVListener.js +1 -403
- package/lib/module/storage/utils/SecureStoreRegistry.js +1 -194
- package/lib/module/storage/utils/asyncStorageCompat.js +1 -81
- package/lib/module/storage/utils/browserStorageLifecycle.js +1 -6
- package/lib/module/storage/utils/browserStorageLifecycle.web.js +1 -66
- package/lib/module/storage/utils/clearAllStorage.js +1 -43
- package/lib/module/storage/utils/deleteStorageKeys.js +1 -78
- package/lib/module/storage/utils/lineDiff.js +1 -12
- package/lib/module/storage/utils/mmkvAvailability.js +1 -56
- package/lib/module/storage/utils/mmkvAvailability.web.js +1 -15
- package/lib/module/storage/utils/mmkvTypeDetection.js +1 -148
- package/lib/module/storage/utils/requiredStorageKeyScope.js +1 -13
- package/lib/module/storage/utils/setStorageValue.js +1 -209
- package/lib/module/storage/utils/snapshotProvider.js +1 -380
- package/lib/module/storage/utils/storageActionHelpers.js +1 -42
- package/lib/module/storage/utils/storageBrowserRefresh.js +1 -44
- package/lib/module/storage/utils/storageLabels.js +1 -9
- package/lib/module/storage/utils/storageLabels.web.js +1 -9
- package/lib/module/storage/utils/storageOps.js +1 -46
- package/lib/module/storage/utils/storageQueryUtils.js +1 -30
- package/lib/module/storage/utils/storageTimeTravelUtils.js +1 -281
- package/lib/module/storage/utils/valueType.js +1 -76
- package/lib/web/index.mjs +2 -3
- package/package.json +6 -10
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +0 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +0 -1
- package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +0 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +0 -1
- package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +0 -1
- package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +0 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageEventActionButton.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageEventCard.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageSaveNotice.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageSection.d.ts.map +0 -1
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +0 -1
- package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +0 -1
- package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +0 -1
- package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +0 -1
- package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +0 -1
- package/lib/typescript/storage/hooks/useStorageEvents.d.ts.map +0 -1
- package/lib/typescript/storage/stores/storageEventStore.d.ts.map +0 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/storage/types.d.ts.map +0 -1
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +0 -1
- package/lib/typescript/storage/utils/AsyncStorageListener.web.d.ts.map +0 -1
- package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +0 -1
- package/lib/typescript/storage/utils/MMKVListener.d.ts.map +0 -1
- package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts.map +0 -1
- package/lib/typescript/storage/utils/asyncStorageCompat.d.ts.map +0 -1
- package/lib/typescript/storage/utils/browserStorageLifecycle.d.ts.map +0 -1
- package/lib/typescript/storage/utils/browserStorageLifecycle.web.d.ts.map +0 -1
- package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +0 -1
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +0 -1
- package/lib/typescript/storage/utils/lineDiff.d.ts.map +0 -1
- package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +0 -1
- package/lib/typescript/storage/utils/mmkvAvailability.web.d.ts.map +0 -1
- package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +0 -1
- package/lib/typescript/storage/utils/requiredStorageKeyScope.d.ts.map +0 -1
- package/lib/typescript/storage/utils/setStorageValue.d.ts.map +0 -1
- package/lib/typescript/storage/utils/snapshotProvider.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageLabels.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageLabels.web.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageOps.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +0 -1
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +0 -1
- package/lib/typescript/storage/utils/valueType.d.ts.map +0 -1
|
@@ -1,113 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { storageLabels } from '../utils/storageLabels';
|
|
4
|
-
/**
|
|
5
|
-
* StorageEventCard
|
|
6
|
-
*
|
|
7
|
-
* Shared card component for displaying storage events.
|
|
8
|
-
* Used by both Storage DevTools and Events DevTools for consistent UI.
|
|
9
|
-
* Uses CompactRow for consistent layout with Redux/React Query cards.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { memo } from "react";
|
|
13
|
-
import { RelativeTime, macOSColors, parseValue, CompactRow } from "@buoy-gg/shared-ui";
|
|
14
|
-
import { translateStorageAction } from "../utils/storageActionHelpers";
|
|
15
|
-
|
|
16
|
-
/** Value type for storage events */
|
|
17
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
-
/**
|
|
19
|
-
* Compute value type from a value
|
|
20
|
-
*/
|
|
21
|
-
export function getValueType(value) {
|
|
22
|
-
const parsed = parseValue(value);
|
|
23
|
-
if (parsed === null) return "null";
|
|
24
|
-
if (parsed === undefined) return "undefined";
|
|
25
|
-
if (Array.isArray(parsed)) return "array";
|
|
26
|
-
if (typeof parsed === "boolean") return "boolean";
|
|
27
|
-
if (typeof parsed === "number") return "number";
|
|
28
|
-
if (typeof parsed === "string") return "string";
|
|
29
|
-
if (typeof parsed === "object") return "object";
|
|
30
|
-
return "undefined";
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Get color for action type
|
|
34
|
-
*/
|
|
35
|
-
function getActionColor(action) {
|
|
36
|
-
switch (action) {
|
|
37
|
-
// AsyncStorage & MMKV - Set operations
|
|
38
|
-
case "setItem":
|
|
39
|
-
case "multiSet":
|
|
40
|
-
case "set.string":
|
|
41
|
-
case "set.number":
|
|
42
|
-
case "set.boolean":
|
|
43
|
-
case "set.buffer":
|
|
44
|
-
return macOSColors.semantic.success;
|
|
45
|
-
|
|
46
|
-
// AsyncStorage & MMKV - Remove/Delete operations
|
|
47
|
-
case "removeItem":
|
|
48
|
-
case "multiRemove":
|
|
49
|
-
case "clear":
|
|
50
|
-
case "delete":
|
|
51
|
-
case "clearAll":
|
|
52
|
-
return macOSColors.semantic.error;
|
|
53
|
-
|
|
54
|
-
// AsyncStorage - Merge operations
|
|
55
|
-
case "mergeItem":
|
|
56
|
-
case "multiMerge":
|
|
57
|
-
return macOSColors.semantic.info;
|
|
58
|
-
|
|
59
|
-
// MMKV - Get operations
|
|
60
|
-
case "get.string":
|
|
61
|
-
case "get.number":
|
|
62
|
-
case "get.boolean":
|
|
63
|
-
case "get.buffer":
|
|
64
|
-
return macOSColors.semantic.warning;
|
|
65
|
-
default:
|
|
66
|
-
return macOSColors.text.muted;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Get storage type label for sublabel
|
|
72
|
-
*/
|
|
73
|
-
function getStorageTypeSublabel(storageTypes, totalOps) {
|
|
74
|
-
const types = Array.from(storageTypes).map(t => t === "async" ? storageLabels.async : storageLabels.mmkv).join("/");
|
|
75
|
-
return `${types} · ${totalOps} op${totalOps !== 1 ? "s" : ""}`;
|
|
76
|
-
}
|
|
77
|
-
export const StorageEventCard = /*#__PURE__*/memo(function StorageEventCard({
|
|
78
|
-
data,
|
|
79
|
-
onPress,
|
|
80
|
-
isSelected = false,
|
|
81
|
-
showChevron = true,
|
|
82
|
-
testID
|
|
83
|
-
}) {
|
|
84
|
-
const actionColor = getActionColor(data.lastAction);
|
|
85
|
-
const actionLabel = translateStorageAction(data.lastAction);
|
|
86
|
-
const totalOps = data.totalOperations ?? 1;
|
|
87
|
-
return /*#__PURE__*/_jsx(CompactRow
|
|
88
|
-
// Status section (left) - shows "Storage" with action-colored dot
|
|
89
|
-
, {
|
|
90
|
-
statusDotColor: actionColor,
|
|
91
|
-
statusLabel: "Storage",
|
|
92
|
-
statusSublabel: getStorageTypeSublabel(data.storageTypes, totalOps)
|
|
93
|
-
// Content section (middle) - shows the key name
|
|
94
|
-
,
|
|
95
|
-
primaryText: data.key
|
|
96
|
-
// Badge section (right) - shows action label (SET, REMOVE, etc.)
|
|
97
|
-
,
|
|
98
|
-
badgeText: actionLabel,
|
|
99
|
-
badgeColor: actionColor,
|
|
100
|
-
showChevron: showChevron
|
|
101
|
-
// Bottom right - timestamp
|
|
102
|
-
,
|
|
103
|
-
bottomRightText: /*#__PURE__*/_jsx(RelativeTime, {
|
|
104
|
-
timestamp: data.lastEventTimestamp
|
|
105
|
-
})
|
|
106
|
-
// Interaction
|
|
107
|
-
,
|
|
108
|
-
isSelected: isSelected,
|
|
109
|
-
onPress: onPress,
|
|
110
|
-
testID: testID,
|
|
111
|
-
accessibilityLabel: `${actionLabel} ${data.key}`
|
|
112
|
-
});
|
|
113
|
-
});
|
|
1
|
+
"use strict";import{storageLabels as i}from"../utils/storageLabels";import{memo as f}from"react";import{RelativeTime as b,macOSColors as r,parseValue as g,CompactRow as p}from"@buoy-gg/shared-ui";import{translateStorageAction as y}from"../utils/storageActionHelpers";import{jsx as c}from"react/jsx-runtime";export function getValueType(t){const e=g(t);if(e===null)return"null";if(e===void 0)return"undefined";if(Array.isArray(e))return"array";if(typeof e==="boolean")return"boolean";if(typeof e==="number")return"number";if(typeof e==="string")return"string";if(typeof e==="object")return"object";return"undefined"}function S(t){switch(t){case"setItem":case"multiSet":case"set.string":case"set.number":case"set.boolean":case"set.buffer":return r.semantic.success;case"removeItem":case"multiRemove":case"clear":case"delete":case"clearAll":return r.semantic.error;case"mergeItem":case"multiMerge":return r.semantic.info;case"get.string":case"get.number":case"get.boolean":case"get.buffer":return r.semantic.warning;default:return r.text.muted}}function d(t,e){const s=Array.from(t).map(o=>o==="async"?i.async:i.mmkv).join("/");return`${s} \xB7 ${e} op${e!==1?"s":""}`}export const StorageEventCard=f(function t({data:e,onPress:s,isSelected:o=false,showChevron:u=true,testID:m}){const n=S(e.lastAction);const a=y(e.lastAction);const l=e.totalOperations??1;return c(p,{statusDotColor:n,statusLabel:"Storage",statusSublabel:d(e.storageTypes,l),primaryText:e.key,badgeText:a,badgeColor:n,showChevron:u,bottomRightText:c(b,{timestamp:e.lastEventTimestamp}),isSelected:o,onPress:s,testID:m,accessibilityLabel:`${a} ${e.key}`})});
|
|
@@ -1,490 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* StorageEventDetailContent
|
|
5
|
-
*
|
|
6
|
-
* Detail view for storage events using the shared EventHistoryViewer component.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { View, Text, StyleSheet } from "react-native";
|
|
10
|
-
import { useState, useCallback, useMemo, useEffect, useRef } from "react";
|
|
11
|
-
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
12
|
-
import { formatRelativeTime, macOSColors, night, parseValue, devToolsStorageKeys, GitBranch, EventStepperFooter, EventHistoryViewer, copyToClipboard, ProUpgradeModal, monoFont } from "@buoy-gg/shared-ui";
|
|
13
|
-
import { useIsPro } from "@buoy-gg/license";
|
|
14
|
-
import { ABSENT_VALUE } from "../utils/lineDiff";
|
|
15
|
-
import { ThemedSplitView } from "./DiffViewer/modes/ThemedSplitView";
|
|
16
|
-
import { diffThemes } from "./DiffViewer/themes/diffThemes";
|
|
17
|
-
import { TreeDiffViewer } from "./DiffViewer/TreeDiffViewer";
|
|
18
|
-
import { translateStorageAction } from "../utils/storageActionHelpers";
|
|
19
|
-
import { StorageEventActionButton } from "./StorageEventActionButton";
|
|
20
|
-
import { undoOperation, jumpToState, canUndo } from "../utils/storageTimeTravelUtils";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Compare-bar index for "the state before the oldest event".
|
|
24
|
-
*
|
|
25
|
-
* Not an event — it's reconstructed from that event's own `prevValue`, which
|
|
26
|
-
* every write records. Without it a key's first event had nothing to diff
|
|
27
|
-
* against and both panes showed the same value.
|
|
28
|
-
*/
|
|
29
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
30
|
-
const ORIGIN_INDEX = -1;
|
|
31
|
-
|
|
32
|
-
/** Compare-bar copy for each origin state. Kept in sync with the Flutter port. */
|
|
33
|
-
const ORIGIN_LABELS = {
|
|
34
|
-
prior: {
|
|
35
|
-
label: "BEFORE #1",
|
|
36
|
-
timestamp: "value before first event",
|
|
37
|
-
relativeTime: "not captured",
|
|
38
|
-
badge: "PRIOR"
|
|
39
|
-
},
|
|
40
|
-
absent: {
|
|
41
|
-
label: "NO PRIOR VALUE",
|
|
42
|
-
timestamp: "key did not exist",
|
|
43
|
-
relativeTime: "created here",
|
|
44
|
-
badge: "ABSENT"
|
|
45
|
-
},
|
|
46
|
-
scanned: {
|
|
47
|
-
label: "ALREADY PRESENT",
|
|
48
|
-
timestamp: "value found at startup",
|
|
49
|
-
relativeTime: "not a write",
|
|
50
|
-
badge: "SCANNED"
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// Unified storage event type (supports both AsyncStorage and MMKV)
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get color for storage action
|
|
58
|
-
*/
|
|
59
|
-
function getActionColor(action) {
|
|
60
|
-
switch (action) {
|
|
61
|
-
case "setItem":
|
|
62
|
-
case "multiSet":
|
|
63
|
-
return macOSColors.semantic.success;
|
|
64
|
-
case "removeItem":
|
|
65
|
-
case "multiRemove":
|
|
66
|
-
case "clear":
|
|
67
|
-
return macOSColors.semantic.error;
|
|
68
|
-
case "mergeItem":
|
|
69
|
-
case "multiMerge":
|
|
70
|
-
return macOSColors.semantic.info;
|
|
71
|
-
default:
|
|
72
|
-
return macOSColors.text.muted;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Get the type of a value
|
|
78
|
-
*/
|
|
79
|
-
function getValueType(value) {
|
|
80
|
-
if (value === null) return "null";
|
|
81
|
-
if (value === undefined) return "undefined";
|
|
82
|
-
if (Array.isArray(value)) return "array";
|
|
83
|
-
return typeof value;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Format timestamp with milliseconds
|
|
88
|
-
*/
|
|
89
|
-
function formatTimeWithMs(date) {
|
|
90
|
-
const h = String(date.getHours()).padStart(2, "0");
|
|
91
|
-
const m = String(date.getMinutes()).padStart(2, "0");
|
|
92
|
-
const s = String(date.getSeconds()).padStart(2, "0");
|
|
93
|
-
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
94
|
-
return `${h}:${m}:${s}.${ms}`;
|
|
95
|
-
}
|
|
96
|
-
const SPLIT_VIEW_OPTIONS = {
|
|
97
|
-
hideLineNumbers: false,
|
|
98
|
-
disableWordDiff: false,
|
|
99
|
-
showDiffOnly: false,
|
|
100
|
-
compareMethod: "words",
|
|
101
|
-
contextLines: 3,
|
|
102
|
-
lineOffset: 0
|
|
103
|
-
};
|
|
104
|
-
const SPLIT_VIEW_DIFFERENCES = [];
|
|
105
|
-
export function StorageEventDetailContent({
|
|
106
|
-
conversation,
|
|
107
|
-
selectedEventIndex = 0,
|
|
108
|
-
onEventIndexChange = () => {},
|
|
109
|
-
disableInternalFooter = false
|
|
110
|
-
}) {
|
|
111
|
-
// Pro feature gating
|
|
112
|
-
const isPro = useIsPro();
|
|
113
|
-
const [showProModal, setShowProModal] = useState(false);
|
|
114
|
-
|
|
115
|
-
// There is only the diff view now. The current stored value is what the Keys
|
|
116
|
-
// table shows — repeating it behind a toggle here meant two places to look for
|
|
117
|
-
// one fact, and the toggle was the first thing between you and the diff you
|
|
118
|
-
// opened this screen for.
|
|
119
|
-
const [diffMode, setDiffMode] = useState("split");
|
|
120
|
-
|
|
121
|
-
// Compare indices for diff view
|
|
122
|
-
// Matches what the sync effect below computes, so the first frame doesn't
|
|
123
|
-
// briefly pair the oldest event with itself.
|
|
124
|
-
const [leftIndex, setLeftIndex] = useState(Math.max(ORIGIN_INDEX, selectedEventIndex - 1));
|
|
125
|
-
const [rightIndex, setRightIndex] = useState(selectedEventIndex);
|
|
126
|
-
|
|
127
|
-
// Track if preferences have been loaded
|
|
128
|
-
const hasLoadedPreferences = useRef(false);
|
|
129
|
-
|
|
130
|
-
// Get sorted events
|
|
131
|
-
const navigationItems = useMemo(() => conversation.events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()), [conversation.events]);
|
|
132
|
-
const totalEvents = navigationItems.length;
|
|
133
|
-
|
|
134
|
-
// Load saved preferences on mount
|
|
135
|
-
useEffect(() => {
|
|
136
|
-
if (hasLoadedPreferences.current) return;
|
|
137
|
-
const loadPreferences = async () => {
|
|
138
|
-
try {
|
|
139
|
-
const savedDiffMode = await AsyncStorage.getItem(devToolsStorageKeys.storage.diffViewerMode());
|
|
140
|
-
if (savedDiffMode === "split" || savedDiffMode === "tree") {
|
|
141
|
-
setDiffMode(savedDiffMode);
|
|
142
|
-
}
|
|
143
|
-
hasLoadedPreferences.current = true;
|
|
144
|
-
} catch {
|
|
145
|
-
// Failed to load preferences
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
loadPreferences();
|
|
149
|
-
}, []);
|
|
150
|
-
|
|
151
|
-
// Keep compare indices synced to selection. Left may land on ORIGIN_INDEX,
|
|
152
|
-
// which is not an event — it's the state the oldest event overwrote.
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
const newRight = Math.min(totalEvents - 1, Math.max(0, selectedEventIndex));
|
|
155
|
-
const newLeft = Math.max(ORIGIN_INDEX, Math.min(newRight - 1, selectedEventIndex - 1));
|
|
156
|
-
setLeftIndex(newLeft);
|
|
157
|
-
setRightIndex(newRight);
|
|
158
|
-
}, [selectedEventIndex, totalEvents]);
|
|
159
|
-
|
|
160
|
-
// Save diff mode preference when changed
|
|
161
|
-
const handleDiffModeChange = useCallback(async mode => {
|
|
162
|
-
setDiffMode(mode);
|
|
163
|
-
try {
|
|
164
|
-
await AsyncStorage.setItem(devToolsStorageKeys.storage.diffViewerMode(), mode);
|
|
165
|
-
} catch {
|
|
166
|
-
// Failed to save preference
|
|
167
|
-
}
|
|
168
|
-
}, []);
|
|
169
|
-
|
|
170
|
-
// Get current event
|
|
171
|
-
const selectedEvent = navigationItems[selectedEventIndex];
|
|
172
|
-
const action = selectedEvent?.action;
|
|
173
|
-
// Compute type from the parsed value so it matches what we actually render
|
|
174
|
-
|
|
175
|
-
// Get events for diff comparison. leftIndex === ORIGIN_INDEX means "the state
|
|
176
|
-
// before the oldest event", which has no event object of its own.
|
|
177
|
-
const isOriginLeft = leftIndex === ORIGIN_INDEX;
|
|
178
|
-
const leftEvent = isOriginLeft ? undefined : navigationItems[Math.max(0, Math.min(totalEvents - 1, leftIndex))];
|
|
179
|
-
const rightEvent = navigationItems[Math.max(0, Math.min(totalEvents - 1, rightIndex))];
|
|
180
|
-
|
|
181
|
-
// Every write records the value it overwrote (for time travel), so even the
|
|
182
|
-
// very first event can be diffed against what was actually there. Three
|
|
183
|
-
// distinct origins, and conflating them would misinform:
|
|
184
|
-
// PRIOR — the write replaced a real value (including `[]`, `""`, `null`)
|
|
185
|
-
// ABSENT — the write created the key; there was nothing before it
|
|
186
|
-
// SCANNED — not a write at all, just the startup snapshot of a key that
|
|
187
|
-
// already existed, so nothing changed at this event
|
|
188
|
-
const originKind = useMemo(() => {
|
|
189
|
-
const data = rightEvent?.data;
|
|
190
|
-
if (data?.initialScan) return "scanned";
|
|
191
|
-
return data?.prevValue === null || data?.prevValue === undefined ? "absent" : "prior";
|
|
192
|
-
}, [rightEvent]);
|
|
193
|
-
const originValue = useMemo(() => {
|
|
194
|
-
if (originKind === "absent") return ABSENT_VALUE;
|
|
195
|
-
// A scanned key was already holding the event's own value, so diffing
|
|
196
|
-
// against it correctly reports no change.
|
|
197
|
-
if (originKind === "scanned") return parseValue(rightEvent?.data?.value);
|
|
198
|
-
return parseValue(rightEvent?.data?.prevValue);
|
|
199
|
-
}, [originKind, rightEvent]);
|
|
200
|
-
|
|
201
|
-
// Create event display info for compare bar
|
|
202
|
-
const leftEventInfo = useMemo(() => isOriginLeft ? {
|
|
203
|
-
index: ORIGIN_INDEX,
|
|
204
|
-
label: ORIGIN_LABELS[originKind].label,
|
|
205
|
-
timestamp: ORIGIN_LABELS[originKind].timestamp,
|
|
206
|
-
relativeTime: ORIGIN_LABELS[originKind].relativeTime,
|
|
207
|
-
badge: /*#__PURE__*/_jsx(View, {
|
|
208
|
-
style: [styles.actionBadgeSmall, {
|
|
209
|
-
backgroundColor: `${night.color.textTertiary}20`
|
|
210
|
-
}],
|
|
211
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
212
|
-
style: [styles.actionTextSmall, {
|
|
213
|
-
color: night.color.textTertiary
|
|
214
|
-
}],
|
|
215
|
-
children: ORIGIN_LABELS[originKind].badge
|
|
216
|
-
})
|
|
217
|
-
})
|
|
218
|
-
} : {
|
|
219
|
-
index: leftIndex,
|
|
220
|
-
label: `#${leftIndex + 1} / ${totalEvents}`,
|
|
221
|
-
timestamp: leftEvent ? formatTimeWithMs(leftEvent.timestamp) : "",
|
|
222
|
-
relativeTime: leftEvent ? formatRelativeTime(leftEvent.timestamp) : "",
|
|
223
|
-
badge: leftEvent ? /*#__PURE__*/_jsx(View, {
|
|
224
|
-
style: [styles.actionBadgeSmall, {
|
|
225
|
-
backgroundColor: `${getActionColor(leftEvent.action)}20`
|
|
226
|
-
}],
|
|
227
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
228
|
-
style: [styles.actionTextSmall, {
|
|
229
|
-
color: getActionColor(leftEvent.action)
|
|
230
|
-
}],
|
|
231
|
-
children: translateStorageAction(leftEvent.action)
|
|
232
|
-
})
|
|
233
|
-
}) : undefined
|
|
234
|
-
}, [leftIndex, leftEvent, totalEvents, isOriginLeft, originKind]);
|
|
235
|
-
const rightEventInfo = useMemo(() => ({
|
|
236
|
-
index: rightIndex,
|
|
237
|
-
label: `#${rightIndex + 1} / ${totalEvents}`,
|
|
238
|
-
timestamp: rightEvent ? formatTimeWithMs(rightEvent.timestamp) : "",
|
|
239
|
-
relativeTime: rightEvent ? formatRelativeTime(rightEvent.timestamp) : "",
|
|
240
|
-
badge: rightEvent ? /*#__PURE__*/_jsx(View, {
|
|
241
|
-
style: [styles.actionBadgeSmall, {
|
|
242
|
-
backgroundColor: `${getActionColor(rightEvent.action)}20`
|
|
243
|
-
}],
|
|
244
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
245
|
-
style: [styles.actionTextSmall, {
|
|
246
|
-
color: getActionColor(rightEvent.action)
|
|
247
|
-
}],
|
|
248
|
-
children: translateStorageAction(rightEvent.action)
|
|
249
|
-
})
|
|
250
|
-
}) : undefined
|
|
251
|
-
}), [rightIndex, rightEvent, totalEvents]);
|
|
252
|
-
|
|
253
|
-
// Diff mode tabs
|
|
254
|
-
const diffModeTabs = useMemo(() => [{
|
|
255
|
-
key: "tree",
|
|
256
|
-
label: "TREE VIEW"
|
|
257
|
-
}, {
|
|
258
|
-
key: "split",
|
|
259
|
-
label: "SPLIT VIEW"
|
|
260
|
-
}], []);
|
|
261
|
-
|
|
262
|
-
// Navigation handlers for compare bar
|
|
263
|
-
const handleLeftPrevious = useCallback(() => {
|
|
264
|
-
// Stops at ORIGIN_INDEX — one step past event #1 is the state it overwrote.
|
|
265
|
-
if (leftIndex > ORIGIN_INDEX) {
|
|
266
|
-
setLeftIndex(leftIndex - 1);
|
|
267
|
-
}
|
|
268
|
-
}, [leftIndex]);
|
|
269
|
-
const handleLeftNext = useCallback(() => {
|
|
270
|
-
if (leftIndex < rightIndex - 1) {
|
|
271
|
-
setLeftIndex(leftIndex + 1);
|
|
272
|
-
}
|
|
273
|
-
}, [leftIndex, rightIndex]);
|
|
274
|
-
const handleRightPrevious = useCallback(() => {
|
|
275
|
-
if (rightIndex > leftIndex + 1) {
|
|
276
|
-
setRightIndex(rightIndex - 1);
|
|
277
|
-
}
|
|
278
|
-
}, [rightIndex, leftIndex]);
|
|
279
|
-
const handleRightNext = useCallback(() => {
|
|
280
|
-
if (rightIndex < totalEvents - 1) {
|
|
281
|
-
setRightIndex(rightIndex + 1);
|
|
282
|
-
}
|
|
283
|
-
}, [rightIndex, totalEvents]);
|
|
284
|
-
|
|
285
|
-
// Memoized so the diff viewers don't see new oldValue/newValue refs on every
|
|
286
|
-
// re-render. parseValue runs JSON.parse, which would otherwise produce fresh
|
|
287
|
-
// object identities every time and force expensive recomputations downstream
|
|
288
|
-
// (line diff, tree diff, expansion-state effects, etc.).
|
|
289
|
-
const previousValue = useMemo(() => isOriginLeft ? originValue : parseValue(leftEvent?.data?.value ?? null), [isOriginLeft, originValue, leftEvent]);
|
|
290
|
-
const currentValue = useMemo(() => parseValue(rightEvent?.data?.value), [rightEvent]);
|
|
291
|
-
|
|
292
|
-
// Render diff content
|
|
293
|
-
const renderDiffContent = useCallback(() => {
|
|
294
|
-
if (diffMode === "split") {
|
|
295
|
-
return /*#__PURE__*/_jsx(ThemedSplitView, {
|
|
296
|
-
oldValue: previousValue,
|
|
297
|
-
newValue: currentValue,
|
|
298
|
-
differences: SPLIT_VIEW_DIFFERENCES,
|
|
299
|
-
theme: diffThemes.devToolsDefault,
|
|
300
|
-
options: SPLIT_VIEW_OPTIONS,
|
|
301
|
-
showThemeName: false,
|
|
302
|
-
scrollEnabled: false
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Inline: the whole diff section scrolls as one page (see
|
|
307
|
-
// `scrollDiffSection` below), so the tree must not scroll itself.
|
|
308
|
-
return /*#__PURE__*/_jsx(TreeDiffViewer, {
|
|
309
|
-
oldValue: previousValue,
|
|
310
|
-
newValue: currentValue,
|
|
311
|
-
scrollEnabled: false
|
|
312
|
-
});
|
|
313
|
-
}, [previousValue, currentValue, diffMode]);
|
|
314
|
-
|
|
315
|
-
// Footer navigation handlers
|
|
316
|
-
const handleFooterPrevious = useCallback(() => {
|
|
317
|
-
onEventIndexChange(Math.max(0, selectedEventIndex - 1));
|
|
318
|
-
}, [selectedEventIndex, onEventIndexChange]);
|
|
319
|
-
const handleFooterNext = useCallback(() => {
|
|
320
|
-
onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1));
|
|
321
|
-
}, [selectedEventIndex, totalEvents, onEventIndexChange]);
|
|
322
|
-
|
|
323
|
-
// Time-travel action handlers
|
|
324
|
-
const handleUndo = useCallback(async () => {
|
|
325
|
-
if (!isPro) {
|
|
326
|
-
setShowProModal(true);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (!selectedEvent || selectedEvent.storageType === "mmkv") return;
|
|
330
|
-
try {
|
|
331
|
-
await undoOperation(selectedEvent);
|
|
332
|
-
} catch {
|
|
333
|
-
// Silent fail
|
|
334
|
-
}
|
|
335
|
-
}, [selectedEvent, isPro]);
|
|
336
|
-
const handleJump = useCallback(async () => {
|
|
337
|
-
if (!isPro) {
|
|
338
|
-
setShowProModal(true);
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
if (!selectedEvent || selectedEvent.storageType === "mmkv") return;
|
|
342
|
-
const asyncEvents = navigationItems.filter(e => e.storageType !== "mmkv");
|
|
343
|
-
const targetIndex = asyncEvents.findIndex(e => e.timestamp.getTime() === selectedEvent.timestamp.getTime());
|
|
344
|
-
if (targetIndex === -1) return;
|
|
345
|
-
try {
|
|
346
|
-
await jumpToState(asyncEvents, targetIndex);
|
|
347
|
-
} catch {
|
|
348
|
-
// Silent fail
|
|
349
|
-
}
|
|
350
|
-
}, [selectedEvent, navigationItems, isPro]);
|
|
351
|
-
const handleCopy = useCallback(async () => {
|
|
352
|
-
if (!selectedEvent) return;
|
|
353
|
-
const eventData = {
|
|
354
|
-
action: selectedEvent.action,
|
|
355
|
-
timestamp: selectedEvent.timestamp.toISOString(),
|
|
356
|
-
data: selectedEvent.data
|
|
357
|
-
};
|
|
358
|
-
await copyToClipboard(eventData);
|
|
359
|
-
}, [selectedEvent]);
|
|
360
|
-
|
|
361
|
-
// Check if current event supports actions
|
|
362
|
-
const isAsyncStorageEvent = selectedEvent?.storageType !== "mmkv";
|
|
363
|
-
const canUndoEvent = isAsyncStorageEvent && selectedEvent && canUndo(selectedEvent);
|
|
364
|
-
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
365
|
-
children: [isAsyncStorageEvent && /*#__PURE__*/_jsxs(View, {
|
|
366
|
-
style: styles.actionButtonsContainer,
|
|
367
|
-
children: [/*#__PURE__*/_jsx(StorageEventActionButton, {
|
|
368
|
-
type: "undo",
|
|
369
|
-
text: "UNDO",
|
|
370
|
-
onPress: handleUndo,
|
|
371
|
-
disabled: !canUndoEvent,
|
|
372
|
-
locked: !isPro
|
|
373
|
-
}), /*#__PURE__*/_jsx(StorageEventActionButton, {
|
|
374
|
-
type: "jump",
|
|
375
|
-
text: "JUMP",
|
|
376
|
-
onPress: handleJump,
|
|
377
|
-
locked: !isPro
|
|
378
|
-
}), /*#__PURE__*/_jsx(StorageEventActionButton, {
|
|
379
|
-
type: "copy",
|
|
380
|
-
text: "COPY",
|
|
381
|
-
onPress: handleCopy
|
|
382
|
-
})]
|
|
383
|
-
}), /*#__PURE__*/_jsx(EventHistoryViewer
|
|
384
|
-
// Diff only: no `renderCurrentView`, so the viewer drops the toggle
|
|
385
|
-
// cards entirely rather than showing a one-option switch.
|
|
386
|
-
, {
|
|
387
|
-
activeView: "diff",
|
|
388
|
-
diffViewLabel: "DIFF VIEW",
|
|
389
|
-
diffViewDescription: "Compare changes between versions",
|
|
390
|
-
diffViewIcon: GitBranch
|
|
391
|
-
// Diff View
|
|
392
|
-
,
|
|
393
|
-
diffModeTabs: diffModeTabs,
|
|
394
|
-
activeDiffMode: diffMode,
|
|
395
|
-
onDiffModeChange: handleDiffModeChange,
|
|
396
|
-
renderDiffContent: renderDiffContent
|
|
397
|
-
// Both diff modes render inline now, so the whole section scrolls as
|
|
398
|
-
// one page rather than confining the rows to a nested pane.
|
|
399
|
-
,
|
|
400
|
-
scrollDiffSection: true
|
|
401
|
-
// Compare Bar
|
|
402
|
-
,
|
|
403
|
-
leftEvent: leftEventInfo,
|
|
404
|
-
rightEvent: rightEventInfo,
|
|
405
|
-
showCompareNavigation: true,
|
|
406
|
-
onLeftPrevious: handleLeftPrevious,
|
|
407
|
-
onLeftNext: handleLeftNext,
|
|
408
|
-
onRightPrevious: handleRightPrevious,
|
|
409
|
-
onRightNext: handleRightNext,
|
|
410
|
-
canLeftPrevious: leftIndex > ORIGIN_INDEX,
|
|
411
|
-
canLeftNext: leftIndex < rightIndex - 1,
|
|
412
|
-
canRightPrevious: rightIndex > leftIndex + 1,
|
|
413
|
-
canRightNext: rightIndex < totalEvents - 1
|
|
414
|
-
// Footer
|
|
415
|
-
,
|
|
416
|
-
disableInternalFooter: disableInternalFooter,
|
|
417
|
-
footerCurrentIndex: selectedEventIndex,
|
|
418
|
-
footerTotalItems: totalEvents,
|
|
419
|
-
footerItemLabel: "Event",
|
|
420
|
-
footerSubtitle: formatRelativeTime(selectedEvent?.timestamp),
|
|
421
|
-
onFooterPrevious: handleFooterPrevious,
|
|
422
|
-
onFooterNext: handleFooterNext
|
|
423
|
-
}), /*#__PURE__*/_jsx(ProUpgradeModal, {
|
|
424
|
-
visible: showProModal,
|
|
425
|
-
onClose: () => setShowProModal(false),
|
|
426
|
-
featureName: "Storage Time-Travel"
|
|
427
|
-
})]
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* External footer component for modal use
|
|
433
|
-
*/
|
|
434
|
-
export function StorageEventDetailFooter({
|
|
435
|
-
conversation,
|
|
436
|
-
selectedEventIndex = 0,
|
|
437
|
-
onEventIndexChange = () => {},
|
|
438
|
-
applySafeAreaInset = true
|
|
439
|
-
}) {
|
|
440
|
-
const navigationItems = conversation.events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
441
|
-
const totalEvents = navigationItems.length;
|
|
442
|
-
return /*#__PURE__*/_jsx(EventStepperFooter, {
|
|
443
|
-
currentIndex: selectedEventIndex,
|
|
444
|
-
totalItems: totalEvents,
|
|
445
|
-
onPrevious: () => onEventIndexChange(Math.max(0, selectedEventIndex - 1)),
|
|
446
|
-
onNext: () => onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1)),
|
|
447
|
-
itemLabel: "Event",
|
|
448
|
-
subtitle: formatRelativeTime(navigationItems[selectedEventIndex]?.timestamp),
|
|
449
|
-
applySafeAreaInset: applySafeAreaInset
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
const styles = StyleSheet.create({
|
|
453
|
-
// Action Buttons
|
|
454
|
-
actionButtonsContainer: {
|
|
455
|
-
flexDirection: "row",
|
|
456
|
-
justifyContent: "center",
|
|
457
|
-
alignItems: "center",
|
|
458
|
-
gap: 8,
|
|
459
|
-
paddingHorizontal: 12,
|
|
460
|
-
paddingVertical: 10,
|
|
461
|
-
backgroundColor: night.color.surface,
|
|
462
|
-
borderBottomWidth: 1,
|
|
463
|
-
borderBottomColor: night.color.border
|
|
464
|
-
},
|
|
465
|
-
// Content Card
|
|
466
|
-
valueBox: {
|
|
467
|
-
backgroundColor: night.color.bg,
|
|
468
|
-
borderRadius: 6,
|
|
469
|
-
borderWidth: 1,
|
|
470
|
-
borderColor: night.color.border,
|
|
471
|
-
padding: 8
|
|
472
|
-
},
|
|
473
|
-
valueText: {
|
|
474
|
-
fontSize: 12,
|
|
475
|
-
color: night.color.text,
|
|
476
|
-
fontFamily: monoFont,
|
|
477
|
-
lineHeight: 18
|
|
478
|
-
},
|
|
479
|
-
// Action badges for compare bar
|
|
480
|
-
actionBadgeSmall: {
|
|
481
|
-
paddingHorizontal: 6,
|
|
482
|
-
paddingVertical: 1,
|
|
483
|
-
borderRadius: 3
|
|
484
|
-
},
|
|
485
|
-
actionTextSmall: {
|
|
486
|
-
fontSize: 8,
|
|
487
|
-
fontWeight: "700",
|
|
488
|
-
fontFamily: monoFont
|
|
489
|
-
}
|
|
490
|
-
});
|
|
1
|
+
"use strict";import{View as w,Text as k,StyleSheet as ft}from"react-native";import{useState as P,useCallback as f,useMemo as g,useEffect as j,useRef as dt}from"react";import H from"@react-native-async-storage/async-storage";import{formatRelativeTime as E,macOSColors as I,night as h,parseValue as N,devToolsStorageKeys as z,GitBranch as ut,EventStepperFooter as pt,EventHistoryViewer as gt,copyToClipboard as ht,ProUpgradeModal as St,monoFont as G}from"@buoy-gg/shared-ui";import{useIsPro as bt}from"@buoy-gg/license";import{ABSENT_VALUE as Tt}from"../utils/lineDiff";import{ThemedSplitView as vt}from"./DiffViewer/modes/ThemedSplitView";import{diffThemes as yt}from"./DiffViewer/themes/diffThemes";import{TreeDiffViewer as xt}from"./DiffViewer/TreeDiffViewer";import{translateStorageAction as J}from"../utils/storageActionHelpers";import{StorageEventActionButton as B}from"./StorageEventActionButton";import{undoOperation as Mt,jumpToState as Vt,canUndo as wt}from"../utils/storageTimeTravelUtils";import{jsx as l,jsxs as K,Fragment as Pt}from"react/jsx-runtime";const T=-1;const R={prior:{label:"BEFORE #1",timestamp:"value before first event",relativeTime:"not captured",badge:"PRIOR"},absent:{label:"NO PRIOR VALUE",timestamp:"key did not exist",relativeTime:"created here",badge:"ABSENT"},scanned:{label:"ALREADY PRESENT",timestamp:"value found at startup",relativeTime:"not a write",badge:"SCANNED"}};function C(a){switch(a){case"setItem":case"multiSet":return I.semantic.success;case"removeItem":case"multiRemove":case"clear":return I.semantic.error;case"mergeItem":case"multiMerge":return I.semantic.info;default:return I.text.muted}}function jt(a){if(a===null)return"null";if(a===void 0)return"undefined";if(Array.isArray(a))return"array";return typeof a}function Y(a){const t=String(a.getHours()).padStart(2,"0");const u=String(a.getMinutes()).padStart(2,"0");const v=String(a.getSeconds()).padStart(2,"0");const c=String(a.getMilliseconds()).padStart(3,"0");return`${t}:${u}:${v}.${c}`}const Et={hideLineNumbers:false,disableWordDiff:false,showDiffOnly:false,compareMethod:"words",contextLines:3,lineOffset:0};const It=[];export function StorageEventDetailContent({conversation:a,selectedEventIndex:t=0,onEventIndexChange:u=()=>{},disableInternalFooter:v=false}){const c=bt();const[V,y]=P(false);const[x,$]=P("split");const[r,D]=P(Math.max(T,t-1));const[i,L]=P(t);const W=dt(false);const b=g(()=>a.events.sort((o,d)=>o.timestamp.getTime()-d.timestamp.getTime()),[a.events]);const s=b.length;j(()=>{if(W.current)return;const o=async()=>{try{const d=await H.getItem(z.storage.diffViewerMode());if(d==="split"||d==="tree"){$(d)}W.current=true}catch{}};o()},[]);j(()=>{const o=Math.min(s-1,Math.max(0,t));const d=Math.max(T,Math.min(o-1,t-1));D(d);L(o)},[t,s]);const X=f(async o=>{$(o);try{await H.setItem(z.storage.diffViewerMode(),o)}catch{}},[]);const e=b[t];const Nt=e?.action;const M=r===T;const m=M?void 0:b[Math.max(0,Math.min(s-1,r))];const n=b[Math.max(0,Math.min(s-1,i))];const p=g(()=>{const o=n?.data;if(o?.initialScan)return"scanned";return o?.prevValue===null||o?.prevValue===void 0?"absent":"prior"},[n]);const _=g(()=>{if(p==="absent")return Tt;if(p==="scanned")return N(n?.data?.value);return N(n?.data?.prevValue)},[p,n]);const q=g(()=>M?{index:T,label:R[p].label,timestamp:R[p].timestamp,relativeTime:R[p].relativeTime,badge:l(w,{style:[S.actionBadgeSmall,{backgroundColor:`${h.color.textTertiary}20`}],children:l(k,{style:[S.actionTextSmall,{color:h.color.textTertiary}],children:R[p].badge})})}:{index:r,label:`#${r+1} / ${s}`,timestamp:m?Y(m.timestamp):"",relativeTime:m?E(m.timestamp):"",badge:m?l(w,{style:[S.actionBadgeSmall,{backgroundColor:`${C(m.action)}20`}],children:l(k,{style:[S.actionTextSmall,{color:C(m.action)}],children:J(m.action)})}):void 0},[r,m,s,M,p]);const Q=g(()=>({index:i,label:`#${i+1} / ${s}`,timestamp:n?Y(n.timestamp):"",relativeTime:n?E(n.timestamp):"",badge:n?l(w,{style:[S.actionBadgeSmall,{backgroundColor:`${C(n.action)}20`}],children:l(k,{style:[S.actionTextSmall,{color:C(n.action)}],children:J(n.action)})}):void 0}),[i,n,s]);const Z=g(()=>[{key:"tree",label:"TREE VIEW"},{key:"split",label:"SPLIT VIEW"}],[]);const tt=f(()=>{if(r>T){D(r-1)}},[r]);const et=f(()=>{if(r<i-1){D(r+1)}},[r,i]);const ot=f(()=>{if(i>r+1){L(i-1)}},[i,r]);const at=f(()=>{if(i<s-1){L(i+1)}},[i,s]);const F=g(()=>M?_:N(m?.data?.value??null),[M,_,m]);const A=g(()=>N(n?.data?.value),[n]);const rt=f(()=>{if(x==="split"){return l(vt,{oldValue:F,newValue:A,differences:It,theme:yt.devToolsDefault,options:Et,showThemeName:false,scrollEnabled:false})}return l(xt,{oldValue:F,newValue:A,scrollEnabled:false})},[F,A,x]);const nt=f(()=>{u(Math.max(0,t-1))},[t,u]);const it=f(()=>{u(Math.min(s-1,t+1))},[t,s,u]);const st=f(async()=>{if(!c){y(true);return}if(!e||e.storageType==="mmkv")return;try{await Mt(e)}catch{}},[e,c]);const lt=f(async()=>{if(!c){y(true);return}if(!e||e.storageType==="mmkv")return;const o=b.filter(O=>O.storageType!=="mmkv");const d=o.findIndex(O=>O.timestamp.getTime()===e.timestamp.getTime());if(d===-1)return;try{await Vt(o,d)}catch{}},[e,b,c]);const ct=f(async()=>{if(!e)return;const o={action:e.action,timestamp:e.timestamp.toISOString(),data:e.data};await ht(o)},[e]);const U=e?.storageType!=="mmkv";const mt=U&&e&&wt(e);return K(Pt,{children:[U&&K(w,{style:S.actionButtonsContainer,children:[l(B,{type:"undo",text:"UNDO",onPress:st,disabled:!mt,locked:!c}),l(B,{type:"jump",text:"JUMP",onPress:lt,locked:!c}),l(B,{type:"copy",text:"COPY",onPress:ct})]}),l(gt,{activeView:"diff",diffViewLabel:"DIFF VIEW",diffViewDescription:"Compare changes between versions",diffViewIcon:ut,diffModeTabs:Z,activeDiffMode:x,onDiffModeChange:X,renderDiffContent:rt,scrollDiffSection:true,leftEvent:q,rightEvent:Q,showCompareNavigation:true,onLeftPrevious:tt,onLeftNext:et,onRightPrevious:ot,onRightNext:at,canLeftPrevious:r>T,canLeftNext:r<i-1,canRightPrevious:i>r+1,canRightNext:i<s-1,disableInternalFooter:v,footerCurrentIndex:t,footerTotalItems:s,footerItemLabel:"Event",footerSubtitle:E(e?.timestamp),onFooterPrevious:nt,onFooterNext:it}),l(St,{visible:V,onClose:()=>y(false),featureName:"Storage Time-Travel"})]})}export function StorageEventDetailFooter({conversation:a,selectedEventIndex:t=0,onEventIndexChange:u=()=>{},applySafeAreaInset:v=true}){const c=a.events.sort((y,x)=>y.timestamp.getTime()-x.timestamp.getTime());const V=c.length;return l(pt,{currentIndex:t,totalItems:V,onPrevious:()=>u(Math.max(0,t-1)),onNext:()=>u(Math.min(V-1,t+1)),itemLabel:"Event",subtitle:E(c[t]?.timestamp),applySafeAreaInset:v})}const S=ft.create({actionButtonsContainer:{flexDirection:"row",justifyContent:"center",alignItems:"center",gap:8,paddingHorizontal:12,paddingVertical:10,backgroundColor:h.color.surface,borderBottomWidth:1,borderBottomColor:h.color.border},valueBox:{backgroundColor:h.color.bg,borderRadius:6,borderWidth:1,borderColor:h.color.border,padding:8},valueText:{fontSize:12,color:h.color.text,fontFamily:G,lineHeight:18},actionBadgeSmall:{paddingHorizontal:6,paddingVertical:1,borderRadius:3},actionTextSmall:{fontSize:8,fontWeight:"700",fontFamily:G}});
|