@buoy-gg/storage 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/index.js +77 -1
- package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +10 -3
- package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +26 -8
- package/lib/commonjs/storage/components/GameUIStorageBrowser.js +263 -115
- package/lib/commonjs/storage/components/GameUIStorageStats.js +7 -7
- package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +1 -1
- package/lib/commonjs/storage/components/MMKVInstanceSelector.js +1 -1
- package/lib/commonjs/storage/components/SelectionActionBar.js +2 -36
- package/lib/commonjs/storage/components/StorageActions.js +6 -6
- package/lib/commonjs/storage/components/StorageBrowserMode.js +16 -2
- package/lib/commonjs/storage/components/StorageEventDetailContent.js +106 -149
- package/lib/commonjs/storage/components/StorageEventListener.js +13 -13
- package/lib/commonjs/storage/components/StorageFilterCards.js +13 -135
- package/lib/commonjs/storage/components/StorageKeyCard.js +1 -1
- package/lib/commonjs/storage/components/StorageKeyEditorScreen.js +392 -0
- package/lib/commonjs/storage/components/StorageKeyRow.js +53 -15
- package/lib/commonjs/storage/components/StorageKeySection.js +10 -4
- package/lib/commonjs/storage/components/StorageKeyStats.js +10 -10
- package/lib/commonjs/storage/components/StorageModalWithTabs.js +272 -129
- package/lib/commonjs/storage/components/StorageScalarKeyEditor.js +46 -0
- package/lib/commonjs/storage/components/StorageTypeFilterBar.js +141 -0
- package/lib/commonjs/storage/components/StorageValueEditModal.js +361 -0
- package/lib/commonjs/storage/hooks/useKeyEditor.js +104 -0
- package/lib/commonjs/storage/hooks/useMeasuredWidth.js +74 -0
- package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +70 -15
- package/lib/commonjs/storage/hooks/useTreeDraft.js +107 -0
- package/lib/commonjs/storage/index.js +29 -0
- package/lib/commonjs/storage/stores/storageEventStore.js +4 -2
- package/lib/commonjs/storage/sync/storageSyncAdapter.js +125 -3
- package/lib/commonjs/storage/utils/AsyncStorageListener.js +76 -12
- package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +10 -9
- package/lib/commonjs/storage/utils/MMKVListener.js +40 -4
- package/lib/commonjs/storage/utils/SecureStoreRegistry.js +52 -1
- package/lib/commonjs/storage/utils/deleteStorageKeys.js +53 -0
- package/lib/commonjs/storage/utils/index.js +13 -0
- package/lib/commonjs/storage/utils/lineDiff.js +21 -354
- package/lib/commonjs/storage/utils/setStorageValue.js +218 -0
- package/lib/commonjs/storage/utils/storageBrowserRefresh.js +49 -0
- package/lib/commonjs/storage/utils/storageOps.js +53 -0
- package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +60 -38
- package/lib/commonjs/storage/utils/valueType.js +24 -0
- package/lib/module/index.js +18 -1
- package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +10 -3
- package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +26 -8
- package/lib/module/storage/components/GameUIStorageBrowser.js +266 -118
- package/lib/module/storage/components/GameUIStorageStats.js +7 -7
- package/lib/module/storage/components/MMKVInstanceInfoPanel.js +1 -1
- package/lib/module/storage/components/MMKVInstanceSelector.js +1 -1
- package/lib/module/storage/components/SelectionActionBar.js +2 -36
- package/lib/module/storage/components/StorageActions.js +7 -7
- package/lib/module/storage/components/StorageBrowserMode.js +16 -2
- package/lib/module/storage/components/StorageEventDetailContent.js +110 -151
- package/lib/module/storage/components/StorageEventListener.js +14 -14
- package/lib/module/storage/components/StorageFilterCards.js +13 -135
- package/lib/module/storage/components/StorageKeyCard.js +1 -1
- package/lib/module/storage/components/StorageKeyEditorScreen.js +388 -0
- package/lib/module/storage/components/StorageKeyRow.js +55 -17
- package/lib/module/storage/components/StorageKeySection.js +11 -5
- package/lib/module/storage/components/StorageKeyStats.js +11 -10
- package/lib/module/storage/components/StorageModalWithTabs.js +272 -130
- package/lib/module/storage/components/StorageScalarKeyEditor.js +42 -0
- package/lib/module/storage/components/StorageTypeFilterBar.js +136 -0
- package/lib/module/storage/components/StorageValueEditModal.js +357 -0
- package/lib/module/storage/hooks/useKeyEditor.js +100 -0
- package/lib/module/storage/hooks/useMeasuredWidth.js +71 -0
- package/lib/module/storage/hooks/useSecureStoreKeys.js +71 -16
- package/lib/module/storage/hooks/useTreeDraft.js +103 -0
- package/lib/module/storage/index.js +3 -0
- package/lib/module/storage/stores/storageEventStore.js +4 -2
- package/lib/module/storage/sync/storageSyncAdapter.js +125 -3
- package/lib/module/storage/utils/AsyncStorageListener.js +76 -12
- package/lib/module/storage/utils/MMKVInstanceRegistry.js +8 -7
- package/lib/module/storage/utils/MMKVListener.js +40 -4
- package/lib/module/storage/utils/SecureStoreRegistry.js +52 -1
- package/lib/module/storage/utils/deleteStorageKeys.js +49 -0
- package/lib/module/storage/utils/index.js +3 -0
- package/lib/module/storage/utils/lineDiff.js +7 -354
- package/lib/module/storage/utils/setStorageValue.js +209 -0
- package/lib/module/storage/utils/storageBrowserRefresh.js +44 -0
- package/lib/module/storage/utils/storageOps.js +46 -0
- package/lib/module/storage/utils/storageTimeTravelUtils.js +61 -40
- package/lib/module/storage/utils/valueType.js +23 -0
- package/lib/typescript/index.d.ts +12 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +4 -1
- package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +7 -1
- package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +27 -1
- package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
- package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts +17 -1
- package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +4 -1
- package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageFilterCards.d.ts +9 -3
- package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts +42 -0
- package/lib/typescript/storage/components/StorageKeyEditorScreen.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts +17 -0
- package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts +7 -1
- package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +28 -1
- package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
- package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts +24 -0
- package/lib/typescript/storage/components/StorageScalarKeyEditor.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts +29 -0
- package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +1 -0
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts +36 -0
- package/lib/typescript/storage/components/StorageValueEditModal.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts +51 -0
- package/lib/typescript/storage/hooks/useKeyEditor.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useMeasuredWidth.d.ts +37 -0
- package/lib/typescript/storage/hooks/useMeasuredWidth.d.ts.map +1 -0
- package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts +8 -1
- package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +1 -1
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts +67 -0
- package/lib/typescript/storage/hooks/useTreeDraft.d.ts.map +1 -0
- package/lib/typescript/storage/index.d.ts +2 -0
- package/lib/typescript/storage/index.d.ts.map +1 -1
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts +41 -0
- package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +15 -0
- package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -1
- package/lib/typescript/storage/utils/MMKVListener.d.ts +7 -0
- package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -1
- package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts +20 -0
- package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts.map +1 -1
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts +27 -0
- package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +1 -0
- package/lib/typescript/storage/utils/index.d.ts +1 -0
- package/lib/typescript/storage/utils/index.d.ts.map +1 -1
- package/lib/typescript/storage/utils/lineDiff.d.ts +7 -31
- package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -1
- package/lib/typescript/storage/utils/setStorageValue.d.ts +64 -0
- package/lib/typescript/storage/utils/setStorageValue.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts +28 -0
- package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageOps.d.ts +25 -0
- package/lib/typescript/storage/utils/storageOps.d.ts.map +1 -0
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts +15 -5
- package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +1 -1
- package/lib/typescript/storage/utils/valueType.d.ts +13 -0
- package/lib/typescript/storage/utils/valueType.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -30,7 +30,10 @@ function StorageKeySection({
|
|
|
30
30
|
onViewHistory,
|
|
31
31
|
onHideKey,
|
|
32
32
|
pinnedKeys,
|
|
33
|
-
onTogglePin
|
|
33
|
+
onTogglePin,
|
|
34
|
+
onEditKey,
|
|
35
|
+
onToggleKey,
|
|
36
|
+
editBlockedFor
|
|
34
37
|
}) {
|
|
35
38
|
const [expandedKey, setExpandedKey] = (0, _react.useState)(null);
|
|
36
39
|
const handleKeyPress = (0, _react.useCallback)(storageKey => {
|
|
@@ -85,6 +88,9 @@ function StorageKeySection({
|
|
|
85
88
|
eventCount: eventCountByKey?.[storageKey.key],
|
|
86
89
|
onViewHistory: onViewHistory ? () => onViewHistory(storageKey.key) : undefined,
|
|
87
90
|
onHideKey: onHideKey,
|
|
91
|
+
onEditKey: onEditKey,
|
|
92
|
+
onToggleKey: onToggleKey,
|
|
93
|
+
editBlockedReason: editBlockedFor?.(storageKey),
|
|
88
94
|
isPinned: pinnedKeys?.has(storageKey.key),
|
|
89
95
|
onTogglePin: onTogglePin
|
|
90
96
|
}, uniqueKey);
|
|
@@ -100,15 +106,15 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
100
106
|
// No gap needed, StorageKeyRow has its own margins
|
|
101
107
|
},
|
|
102
108
|
emptySection: {
|
|
103
|
-
backgroundColor: "rgba(
|
|
109
|
+
backgroundColor: "rgba(128, 128, 128, 0.04)",
|
|
104
110
|
borderRadius: 6,
|
|
105
111
|
padding: 16,
|
|
106
112
|
borderWidth: 1,
|
|
107
|
-
borderColor: "rgba(
|
|
113
|
+
borderColor: "rgba(128, 128, 128, 0.1)",
|
|
108
114
|
alignItems: "center"
|
|
109
115
|
},
|
|
110
116
|
emptySectionText: {
|
|
111
|
-
color:
|
|
117
|
+
color: _sharedUi.macOSColors.text.icon,
|
|
112
118
|
fontSize: 11,
|
|
113
119
|
textAlign: "center"
|
|
114
120
|
}
|
|
@@ -259,15 +259,15 @@ function StorageKeyStatsSection({
|
|
|
259
259
|
const styles = _reactNative.StyleSheet.create({
|
|
260
260
|
statsContainer: {
|
|
261
261
|
marginBottom: 24,
|
|
262
|
-
backgroundColor: "rgba(
|
|
262
|
+
backgroundColor: "rgba(128, 128, 128, 0.06)",
|
|
263
263
|
borderRadius: 8,
|
|
264
264
|
padding: 16,
|
|
265
265
|
borderWidth: 1,
|
|
266
|
-
borderColor: "rgba(
|
|
266
|
+
borderColor: "rgba(128, 128, 128, 0.16)"
|
|
267
267
|
},
|
|
268
268
|
// Section titles
|
|
269
269
|
sectionTitle: {
|
|
270
|
-
color:
|
|
270
|
+
color: _sharedUi.buoyColors.textSecondary,
|
|
271
271
|
fontSize: 12,
|
|
272
272
|
fontWeight: "500",
|
|
273
273
|
marginBottom: 16,
|
|
@@ -283,11 +283,11 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
283
283
|
gap: 12
|
|
284
284
|
},
|
|
285
285
|
breakdownItem: {
|
|
286
|
-
backgroundColor: "rgba(
|
|
286
|
+
backgroundColor: "rgba(128, 128, 128, 0.04)",
|
|
287
287
|
padding: 12,
|
|
288
288
|
borderRadius: 8,
|
|
289
289
|
borderWidth: 1,
|
|
290
|
-
borderColor: "rgba(
|
|
290
|
+
borderColor: "rgba(128, 128, 128, 0.1)"
|
|
291
291
|
},
|
|
292
292
|
breakdownItemRow: {
|
|
293
293
|
flexDirection: "row",
|
|
@@ -310,12 +310,12 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
310
310
|
minWidth: 0
|
|
311
311
|
},
|
|
312
312
|
breakdownItemLabel: {
|
|
313
|
-
color:
|
|
313
|
+
color: _sharedUi.macOSColors.text.primary,
|
|
314
314
|
fontSize: 14,
|
|
315
315
|
fontWeight: "500"
|
|
316
316
|
},
|
|
317
317
|
breakdownItemDesc: {
|
|
318
|
-
color:
|
|
318
|
+
color: _sharedUi.buoyColors.textSecondary,
|
|
319
319
|
fontSize: 12
|
|
320
320
|
},
|
|
321
321
|
breakdownItemRight: {
|
|
@@ -326,18 +326,18 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
326
326
|
fontWeight: "600"
|
|
327
327
|
},
|
|
328
328
|
breakdownPercentage: {
|
|
329
|
-
color:
|
|
329
|
+
color: _sharedUi.macOSColors.text.icon,
|
|
330
330
|
fontSize: 10
|
|
331
331
|
},
|
|
332
332
|
// Empty state
|
|
333
333
|
emptyState: {
|
|
334
334
|
padding: 16,
|
|
335
|
-
backgroundColor: "rgba(
|
|
335
|
+
backgroundColor: "rgba(128, 128, 128, 0.04)",
|
|
336
336
|
borderRadius: 6,
|
|
337
337
|
alignItems: "center"
|
|
338
338
|
},
|
|
339
339
|
emptyStateText: {
|
|
340
|
-
color:
|
|
340
|
+
color: _sharedUi.macOSColors.text.icon,
|
|
341
341
|
fontSize: 11,
|
|
342
342
|
textAlign: "center"
|
|
343
343
|
}
|
|
@@ -5,11 +5,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.FREE_TIER_EVENT_LIMIT = void 0;
|
|
7
7
|
exports.StorageModalWithTabs = StorageModalWithTabs;
|
|
8
|
+
exports.StorageToolView = StorageToolView;
|
|
8
9
|
var _react = require("react");
|
|
9
10
|
var _reactNative = require("react-native");
|
|
10
11
|
var _asyncStorage = _interopRequireDefault(require("@react-native-async-storage/async-storage"));
|
|
11
12
|
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
12
13
|
var _StorageBrowserMode = require("./StorageBrowserMode");
|
|
14
|
+
var _StorageTypeFilterBar = require("./StorageTypeFilterBar");
|
|
13
15
|
var _clearAllStorage = require("../utils/clearAllStorage");
|
|
14
16
|
var _useStorageEvents = require("../hooks/useStorageEvents");
|
|
15
17
|
var _StorageEventDetailContent = require("./StorageEventDetailContent");
|
|
@@ -29,10 +31,16 @@ function StorageModalWithTabs({
|
|
|
29
31
|
onBack,
|
|
30
32
|
onMinimize,
|
|
31
33
|
enableSharedModalDimensions = false,
|
|
32
|
-
requiredStorageKeys = []
|
|
34
|
+
requiredStorageKeys = [],
|
|
35
|
+
inline = false,
|
|
36
|
+
onTabChange,
|
|
37
|
+
onRefresh
|
|
33
38
|
}) {
|
|
34
39
|
const isPro = (0, _license.useIsPro)();
|
|
35
40
|
const [activeTab, setActiveTab] = (0, _react.useState)("browser");
|
|
41
|
+
(0, _react.useEffect)(() => {
|
|
42
|
+
onTabChange?.(activeTab);
|
|
43
|
+
}, [activeTab, onTabChange]);
|
|
36
44
|
|
|
37
45
|
// Storage Browser state
|
|
38
46
|
const [searchQuery, setSearchQuery] = (0, _react.useState)("");
|
|
@@ -58,6 +66,19 @@ function StorageModalWithTabs({
|
|
|
58
66
|
const [showFilters, setShowFilters] = (0, _react.useState)(false);
|
|
59
67
|
const [ignoredPatterns, setIgnoredPatterns] = (0, _react.useState)(new Set(["@react_buoy", "@RNAsyncStorage", "redux-persist", "persist:"]) // Auto-hide dev tool keys by default
|
|
60
68
|
);
|
|
69
|
+
/**
|
|
70
|
+
* Which backend both tabs are looking at.
|
|
71
|
+
*
|
|
72
|
+
* Owned here rather than inside the browser because the Keys and Events tabs
|
|
73
|
+
* answer questions about the same three backends — filter the key list to
|
|
74
|
+
* Keychain, switch to Events to see what wrote to it, and the filter should
|
|
75
|
+
* still be Keychain.
|
|
76
|
+
*
|
|
77
|
+
* Distinct from `enabledStorageTypes` below: that's the persisted "capture
|
|
78
|
+
* this backend at all" setting from the Filters view. This is a view filter,
|
|
79
|
+
* and it never hides data the other one already dropped.
|
|
80
|
+
*/
|
|
81
|
+
const [storageTypeFilter, setStorageTypeFilter] = (0, _react.useState)("all");
|
|
61
82
|
const [enabledStorageTypes, setEnabledStorageTypes] = (0, _react.useState)(new Set(['async', 'mmkv', 'secure']) // All enabled by default
|
|
62
83
|
);
|
|
63
84
|
const [pinnedKeys, setPinnedKeys] = (0, _react.useState)(new Set());
|
|
@@ -69,8 +90,13 @@ function StorageModalWithTabs({
|
|
|
69
90
|
|
|
70
91
|
// isListening = enabled and capturing (has subscribers)
|
|
71
92
|
const isListening = isListeningEnabled && isCapturing;
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
|
|
94
|
+
// Track the modal's presentation mode so the stepper footer only reserves
|
|
95
|
+
// safe-area bottom padding while docked as a bottom sheet. In floating mode
|
|
96
|
+
// the footer isn't pinned to the screen edge, so that inset is dead space.
|
|
97
|
+
const [modalMode, setModalMode] = (0, _react.useState)("bottomSheet");
|
|
98
|
+
const handleModeChange = (0, _react.useCallback)(mode => {
|
|
99
|
+
setModalMode(mode);
|
|
74
100
|
}, []);
|
|
75
101
|
|
|
76
102
|
// Timer removed - using useTickEveryMinute hook instead
|
|
@@ -416,14 +442,34 @@ function StorageModalWithTabs({
|
|
|
416
442
|
return counts;
|
|
417
443
|
}, [conversations]);
|
|
418
444
|
|
|
445
|
+
/**
|
|
446
|
+
* When each key was last written, for the wide table's "Last write" column.
|
|
447
|
+
* Derived from the recorded event stream, NOT from the key read: a storage
|
|
448
|
+
* read has no timestamp of its own, so a key we never witnessed being written
|
|
449
|
+
* has no honest answer here and is simply absent.
|
|
450
|
+
*/
|
|
451
|
+
const lastWriteByKey = (0, _react.useMemo)(() => {
|
|
452
|
+
const at = {};
|
|
453
|
+
conversations.forEach(conv => {
|
|
454
|
+
at[conv.key] = conv.lastEvent.timestamp.getTime();
|
|
455
|
+
});
|
|
456
|
+
return at;
|
|
457
|
+
}, [conversations]);
|
|
458
|
+
|
|
419
459
|
// Selected history conversation (from browser tab "view history")
|
|
420
460
|
const selectedHistoryConversation = (0, _react.useMemo)(() => {
|
|
421
461
|
if (!selectedHistoryKey) return null;
|
|
422
462
|
return conversations.find(c => c.key === selectedHistoryKey) || null;
|
|
423
463
|
}, [selectedHistoryKey, conversations]);
|
|
424
464
|
|
|
425
|
-
|
|
426
|
-
|
|
465
|
+
/**
|
|
466
|
+
* Events passing everything EXCEPT the backend pills.
|
|
467
|
+
*
|
|
468
|
+
* Split out so the pills can show what each one would reveal. Counting the
|
|
469
|
+
* already-filtered list would zero out the three types you didn't pick, which
|
|
470
|
+
* tells you nothing about where to look next.
|
|
471
|
+
*/
|
|
472
|
+
const eventsBeforeTypeFilter = (0, _react.useMemo)(() => {
|
|
427
473
|
return events.filter(event => {
|
|
428
474
|
if (!event.data?.key) return false;
|
|
429
475
|
if (!enabledStorageTypes.has(event.storageType)) return false;
|
|
@@ -431,6 +477,13 @@ function StorageModalWithTabs({
|
|
|
431
477
|
return !Array.from(ignoredPatterns).some(p => key.includes(p));
|
|
432
478
|
});
|
|
433
479
|
}, [events, ignoredPatterns, enabledStorageTypes]);
|
|
480
|
+
const eventTypeCounts = (0, _react.useMemo)(() => (0, _StorageTypeFilterBar.countByStorageType)(eventsBeforeTypeFilter), [eventsBeforeTypeFilter]);
|
|
481
|
+
|
|
482
|
+
// Filtered raw events for the events tab (chronological stream, newest-first)
|
|
483
|
+
const filteredEvents = (0, _react.useMemo)(() => {
|
|
484
|
+
if (storageTypeFilter === "all") return eventsBeforeTypeFilter;
|
|
485
|
+
return eventsBeforeTypeFilter.filter(event => event.storageType === storageTypeFilter);
|
|
486
|
+
}, [eventsBeforeTypeFilter, storageTypeFilter]);
|
|
434
487
|
const getEventsSnapshot = (0, _react.useCallback)(() => {
|
|
435
488
|
return filteredEvents.map(event => ({
|
|
436
489
|
key: event.data?.key,
|
|
@@ -551,10 +604,17 @@ function StorageModalWithTabs({
|
|
|
551
604
|
searchQuery: searchQuery,
|
|
552
605
|
storageDataRef: storageDataRef,
|
|
553
606
|
eventCountByKey: eventCountByKey,
|
|
607
|
+
lastWriteByKey: lastWriteByKey,
|
|
554
608
|
onViewHistory: handleViewHistoryFromBrowser,
|
|
555
609
|
enabledStorageTypes: enabledStorageTypes,
|
|
556
610
|
pinnedKeys: pinnedKeys,
|
|
557
611
|
onTogglePin: handleTogglePin
|
|
612
|
+
// Only a docked sheet sits on the screen's bottom edge; a floating
|
|
613
|
+
// window's own bottom is well above the home indicator.
|
|
614
|
+
,
|
|
615
|
+
applySafeAreaInset: modalMode === "bottomSheet",
|
|
616
|
+
storageTypeFilter: storageTypeFilter,
|
|
617
|
+
onStorageTypeFilterChange: setStorageTypeFilter
|
|
558
618
|
});
|
|
559
619
|
}
|
|
560
620
|
|
|
@@ -570,48 +630,75 @@ function StorageModalWithTabs({
|
|
|
570
630
|
})
|
|
571
631
|
});
|
|
572
632
|
}
|
|
633
|
+
|
|
634
|
+
// The pills render ABOVE the empty state as well as the list. Filtering to
|
|
635
|
+
// a backend that happens to have no events would otherwise hide the only
|
|
636
|
+
// control that undoes it, leaving the tab looking permanently broken.
|
|
637
|
+
const eventTypeFilterBar = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
638
|
+
style: styles.eventFilterBar,
|
|
639
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageTypeFilterBar.StorageTypeFilterBar, {
|
|
640
|
+
active: storageTypeFilter,
|
|
641
|
+
onChange: setStorageTypeFilter,
|
|
642
|
+
counts: eventTypeCounts
|
|
643
|
+
})
|
|
644
|
+
});
|
|
573
645
|
if (filteredEvents.length === 0) {
|
|
646
|
+
const filtered = storageTypeFilter !== "all";
|
|
574
647
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
575
|
-
style: styles.
|
|
576
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
648
|
+
style: styles.flexFill,
|
|
649
|
+
children: [eventTypeFilterBar, /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
650
|
+
style: styles.emptyState,
|
|
651
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Database, {
|
|
652
|
+
size: 48,
|
|
653
|
+
color: _sharedUi.macOSColors.text.muted
|
|
654
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
655
|
+
style: styles.emptyTitle,
|
|
656
|
+
children: !isListening ? "Event listener is paused" : filtered ? `No ${storageTypeFilter} events` : "No storage events yet"
|
|
657
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
658
|
+
style: styles.emptySubtitle,
|
|
659
|
+
children: !isListening ? "Press play to start monitoring" : filtered ? "Other backends have events — pick All Types to see them" : "Storage operations will appear here"
|
|
660
|
+
})]
|
|
585
661
|
})]
|
|
586
662
|
});
|
|
587
663
|
}
|
|
588
|
-
return /*#__PURE__*/(0, _jsxRuntime.
|
|
664
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
589
665
|
style: styles.flexFill,
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
666
|
+
children: [eventTypeFilterBar, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
|
|
667
|
+
style: styles.flexFill,
|
|
668
|
+
data: visibleEvents,
|
|
669
|
+
renderItem: renderEventItem,
|
|
670
|
+
keyExtractor: eventKeyExtractor,
|
|
671
|
+
onEndReachedThreshold: END_REACHED_THRESHOLD,
|
|
672
|
+
contentContainerStyle: styles.listContent,
|
|
673
|
+
ListFooterComponent: lockedEventCount > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
674
|
+
style: styles.lockedBannerContainer,
|
|
675
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProFeatureBanner, {
|
|
676
|
+
featureName: `${lockedEventCount} event${lockedEventCount > 1 ? 's' : ''} locked`,
|
|
677
|
+
description: "Upgrade to Pro to unlock full event history"
|
|
678
|
+
})
|
|
679
|
+
}) : null,
|
|
680
|
+
initialNumToRender: 10,
|
|
681
|
+
maxToRenderPerBatch: 10,
|
|
682
|
+
windowSize: 10
|
|
683
|
+
})]
|
|
605
684
|
});
|
|
606
685
|
};
|
|
607
|
-
|
|
686
|
+
|
|
687
|
+
// The stepper renders nothing for a single-event conversation
|
|
688
|
+
// (EventStepperFooter bails at totalItems <= 1), but an element is truthy
|
|
689
|
+
// either way — so `footerHeight={footerNode ? 68 : 0}` below used to reserve
|
|
690
|
+
// 68px for a footer that drew nothing, leaving a dead band under the content.
|
|
691
|
+
// Gate the node on the event count so the reserve matches what's painted.
|
|
692
|
+
const footerNode = selectedConversation && selectedConversation.events.length > 1 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageEventDetailContent.StorageEventDetailFooter, {
|
|
608
693
|
conversation: selectedConversation,
|
|
609
694
|
selectedEventIndex: selectedEventIndex,
|
|
610
|
-
onEventIndexChange: setSelectedEventIndex
|
|
611
|
-
|
|
695
|
+
onEventIndexChange: setSelectedEventIndex,
|
|
696
|
+
applySafeAreaInset: modalMode === "bottomSheet"
|
|
697
|
+
}) : selectedHistoryConversation && selectedHistoryEventIndex !== null && selectedHistoryConversation.events.length > 1 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageEventDetailContent.StorageEventDetailFooter, {
|
|
612
698
|
conversation: selectedHistoryConversation,
|
|
613
699
|
selectedEventIndex: selectedHistoryEventIndex,
|
|
614
|
-
onEventIndexChange: setSelectedHistoryEventIndex
|
|
700
|
+
onEventIndexChange: setSelectedHistoryEventIndex,
|
|
701
|
+
applySafeAreaInset: modalMode === "bottomSheet"
|
|
615
702
|
}) : null;
|
|
616
703
|
|
|
617
704
|
// Determine the appropriate back handler based on current view state
|
|
@@ -619,6 +706,122 @@ function StorageModalWithTabs({
|
|
|
619
706
|
setSelectedConversationKey(null);
|
|
620
707
|
setSelectedEventIndex(0);
|
|
621
708
|
} : onBack;
|
|
709
|
+
const headerContent = showFilters ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
710
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
711
|
+
onBack: () => setShowFilters(false)
|
|
712
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
713
|
+
title: "Filters"
|
|
714
|
+
})]
|
|
715
|
+
}) : selectedHistoryKey ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
716
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
717
|
+
onBack: selectedHistoryEventIndex !== null ? () => setSelectedHistoryEventIndex(null) : handleBackFromHistory
|
|
718
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
719
|
+
title: `${selectedHistoryKey} History`,
|
|
720
|
+
centered: true
|
|
721
|
+
})]
|
|
722
|
+
}) : selectedConversation ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
723
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
724
|
+
onBack: () => {
|
|
725
|
+
setSelectedConversationKey(null);
|
|
726
|
+
setSelectedEventIndex(0);
|
|
727
|
+
}
|
|
728
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
729
|
+
title: selectedConversation.key
|
|
730
|
+
})]
|
|
731
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
732
|
+
children: [onBack && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
733
|
+
onBack: onBack
|
|
734
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
735
|
+
title: "",
|
|
736
|
+
children: isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SearchBar, {
|
|
737
|
+
value: searchQuery,
|
|
738
|
+
onChange: setSearchQuery,
|
|
739
|
+
onClear: () => {
|
|
740
|
+
setSearchQuery("");
|
|
741
|
+
setIsSearchActive(false);
|
|
742
|
+
},
|
|
743
|
+
placeholder: "Search storage keys...",
|
|
744
|
+
autoFocus: true,
|
|
745
|
+
onSubmitEditing: () => setIsSearchActive(false),
|
|
746
|
+
returnKeyType: "search",
|
|
747
|
+
suggestions: allStorageKeys,
|
|
748
|
+
containerStyle: styles.headerSearchContainer
|
|
749
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
|
|
750
|
+
tabs: [{
|
|
751
|
+
key: "browser",
|
|
752
|
+
label: "Storage"
|
|
753
|
+
}, {
|
|
754
|
+
key: "events",
|
|
755
|
+
label: `Events${filteredEvents.length > 0 ? ` (${filteredEvents.length})` : ""}`
|
|
756
|
+
}],
|
|
757
|
+
activeTab: activeTab,
|
|
758
|
+
onTabChange: tab => {
|
|
759
|
+
setActiveTab(tab);
|
|
760
|
+
setSelectedHistoryKey(null);
|
|
761
|
+
setSelectedHistoryEventIndex(null);
|
|
762
|
+
setSelectedConversationKey(null);
|
|
763
|
+
setSelectedEventIndex(0);
|
|
764
|
+
}
|
|
765
|
+
})
|
|
766
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
|
|
767
|
+
children: [onRefresh && !isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
768
|
+
onPress: onRefresh,
|
|
769
|
+
style: styles.iconButton,
|
|
770
|
+
accessibilityLabel: "Refresh storage",
|
|
771
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
|
|
772
|
+
size: 14,
|
|
773
|
+
color: _sharedUi.macOSColors.text.secondary
|
|
774
|
+
})
|
|
775
|
+
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
776
|
+
onPress: handleToggleFilters,
|
|
777
|
+
style: [styles.iconButton, (ignoredPatterns.size > 0 || enabledStorageTypes.size < 3) && styles.activeFilterButton],
|
|
778
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
|
|
779
|
+
size: 14,
|
|
780
|
+
color: ignoredPatterns.size > 0 || enabledStorageTypes.size < 3 ? _sharedUi.macOSColors.semantic.debug : _sharedUi.macOSColors.text.secondary
|
|
781
|
+
})
|
|
782
|
+
}), activeTab === "browser" && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
783
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
784
|
+
onPress: () => setIsSearchActive(true),
|
|
785
|
+
style: styles.iconButton,
|
|
786
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
|
|
787
|
+
size: 14,
|
|
788
|
+
color: _sharedUi.macOSColors.text.secondary
|
|
789
|
+
})
|
|
790
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
|
|
791
|
+
value: getStorageSnapshot,
|
|
792
|
+
disabled: storageDataRef.current.length === 0,
|
|
793
|
+
buttonStyle: styles.iconButton
|
|
794
|
+
})]
|
|
795
|
+
}), activeTab === "events" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
796
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
|
|
797
|
+
value: getEventsSnapshot,
|
|
798
|
+
disabled: filteredEvents.length === 0,
|
|
799
|
+
buttonStyle: styles.iconButton
|
|
800
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PowerToggleButton, {
|
|
801
|
+
isEnabled: isListening,
|
|
802
|
+
onToggle: handleToggleListening,
|
|
803
|
+
accessibilityLabel: "Toggle storage event monitoring"
|
|
804
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarClearButton, {
|
|
805
|
+
onPress: handleClearEvents,
|
|
806
|
+
buttonStyle: styles.iconButton
|
|
807
|
+
})]
|
|
808
|
+
})]
|
|
809
|
+
})]
|
|
810
|
+
});
|
|
811
|
+
|
|
812
|
+
// The ONLY difference between the phone and the desktop dashboard is which
|
|
813
|
+
// shell the header, body and footer go in. Everything above this is shared.
|
|
814
|
+
if (inline) {
|
|
815
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TickProvider, {
|
|
816
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
817
|
+
style: styles.inlineRoot,
|
|
818
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
819
|
+
style: styles.inlineHeader,
|
|
820
|
+
children: headerContent
|
|
821
|
+
}), renderContent(), footerNode]
|
|
822
|
+
})
|
|
823
|
+
});
|
|
824
|
+
}
|
|
622
825
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TickProvider, {
|
|
623
826
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.JsModal, {
|
|
624
827
|
visible: visible,
|
|
@@ -628,100 +831,7 @@ function StorageModalWithTabs({
|
|
|
628
831
|
persistenceKey: persistenceKey,
|
|
629
832
|
header: {
|
|
630
833
|
showToggleButton: true,
|
|
631
|
-
customContent:
|
|
632
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
633
|
-
onBack: () => setShowFilters(false)
|
|
634
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
635
|
-
title: "Filters"
|
|
636
|
-
})]
|
|
637
|
-
}) : selectedHistoryKey ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
638
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
639
|
-
onBack: selectedHistoryEventIndex !== null ? () => setSelectedHistoryEventIndex(null) : handleBackFromHistory
|
|
640
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
641
|
-
title: `${selectedHistoryKey} History`,
|
|
642
|
-
centered: true
|
|
643
|
-
})]
|
|
644
|
-
}) : selectedConversation ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
645
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
646
|
-
onBack: () => {
|
|
647
|
-
setSelectedConversationKey(null);
|
|
648
|
-
setSelectedEventIndex(0);
|
|
649
|
-
}
|
|
650
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
651
|
-
title: selectedConversation.key
|
|
652
|
-
})]
|
|
653
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
|
|
654
|
-
children: [onBack && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
|
|
655
|
-
onBack: onBack
|
|
656
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
|
|
657
|
-
title: "",
|
|
658
|
-
children: isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.SearchBar, {
|
|
659
|
-
value: searchQuery,
|
|
660
|
-
onChange: setSearchQuery,
|
|
661
|
-
onClear: () => {
|
|
662
|
-
setSearchQuery("");
|
|
663
|
-
setIsSearchActive(false);
|
|
664
|
-
},
|
|
665
|
-
placeholder: "Search storage keys...",
|
|
666
|
-
autoFocus: true,
|
|
667
|
-
onSubmitEditing: () => setIsSearchActive(false),
|
|
668
|
-
returnKeyType: "search",
|
|
669
|
-
suggestions: allStorageKeys,
|
|
670
|
-
containerStyle: styles.headerSearchContainer
|
|
671
|
-
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.TabSelector, {
|
|
672
|
-
tabs: [{
|
|
673
|
-
key: "browser",
|
|
674
|
-
label: "Storage"
|
|
675
|
-
}, {
|
|
676
|
-
key: "events",
|
|
677
|
-
label: `Events${filteredEvents.length > 0 ? ` (${filteredEvents.length})` : ""}`
|
|
678
|
-
}],
|
|
679
|
-
activeTab: activeTab,
|
|
680
|
-
onTabChange: tab => {
|
|
681
|
-
setActiveTab(tab);
|
|
682
|
-
setSelectedHistoryKey(null);
|
|
683
|
-
setSelectedHistoryEventIndex(null);
|
|
684
|
-
setSelectedConversationKey(null);
|
|
685
|
-
setSelectedEventIndex(0);
|
|
686
|
-
}
|
|
687
|
-
})
|
|
688
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
|
|
689
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
690
|
-
onPress: handleToggleFilters,
|
|
691
|
-
style: [styles.iconButton, (ignoredPatterns.size > 0 || enabledStorageTypes.size < 3) && styles.activeFilterButton],
|
|
692
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Filter, {
|
|
693
|
-
size: 14,
|
|
694
|
-
color: ignoredPatterns.size > 0 || enabledStorageTypes.size < 3 ? _sharedUi.macOSColors.semantic.debug : _sharedUi.macOSColors.text.secondary
|
|
695
|
-
})
|
|
696
|
-
}), activeTab === "browser" && !isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
697
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
698
|
-
onPress: () => setIsSearchActive(true),
|
|
699
|
-
style: styles.iconButton,
|
|
700
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
|
|
701
|
-
size: 14,
|
|
702
|
-
color: _sharedUi.macOSColors.text.secondary
|
|
703
|
-
})
|
|
704
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
|
|
705
|
-
value: getStorageSnapshot,
|
|
706
|
-
disabled: storageDataRef.current.length === 0,
|
|
707
|
-
buttonStyle: styles.iconButton
|
|
708
|
-
})]
|
|
709
|
-
}), activeTab === "events" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
710
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
|
|
711
|
-
value: getEventsSnapshot,
|
|
712
|
-
disabled: filteredEvents.length === 0,
|
|
713
|
-
buttonStyle: styles.iconButton
|
|
714
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PowerToggleButton, {
|
|
715
|
-
isEnabled: isListening,
|
|
716
|
-
onToggle: handleToggleListening,
|
|
717
|
-
accessibilityLabel: "Toggle storage event monitoring"
|
|
718
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarClearButton, {
|
|
719
|
-
onPress: handleClearEvents,
|
|
720
|
-
buttonStyle: styles.iconButton
|
|
721
|
-
})]
|
|
722
|
-
})]
|
|
723
|
-
})]
|
|
724
|
-
})
|
|
834
|
+
customContent: headerContent
|
|
725
835
|
},
|
|
726
836
|
onModeChange: handleModeChange,
|
|
727
837
|
enablePersistence: true,
|
|
@@ -735,7 +845,34 @@ function StorageModalWithTabs({
|
|
|
735
845
|
})
|
|
736
846
|
});
|
|
737
847
|
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* The same storage tool as `StorageModalWithTabs`, rendered INLINE for hosts
|
|
851
|
+
* that place tools in a panel rather than a modal. Exists so there is exactly
|
|
852
|
+
* one implementation — the desktop dashboard used to ship its own copy of this
|
|
853
|
+
* component's header, tabs and screen router.
|
|
854
|
+
*/
|
|
855
|
+
function StorageToolView(props = {}) {
|
|
856
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(StorageModalWithTabs, {
|
|
857
|
+
...props,
|
|
858
|
+
inline: true,
|
|
859
|
+
visible: true,
|
|
860
|
+
onClose: () => {}
|
|
861
|
+
});
|
|
862
|
+
}
|
|
738
863
|
const styles = _reactNative.StyleSheet.create({
|
|
864
|
+
inlineRoot: {
|
|
865
|
+
flex: 1,
|
|
866
|
+
minHeight: 0,
|
|
867
|
+
backgroundColor: _sharedUi.macOSColors.background.base
|
|
868
|
+
},
|
|
869
|
+
inlineHeader: {
|
|
870
|
+
paddingHorizontal: 12,
|
|
871
|
+
paddingVertical: 8,
|
|
872
|
+
borderBottomWidth: 1,
|
|
873
|
+
borderBottomColor: _sharedUi.macOSColors.border.default,
|
|
874
|
+
backgroundColor: _sharedUi.macOSColors.background.card
|
|
875
|
+
},
|
|
739
876
|
headerSearchContainer: {
|
|
740
877
|
flex: 1
|
|
741
878
|
},
|
|
@@ -765,6 +902,12 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
765
902
|
marginTop: 8,
|
|
766
903
|
marginHorizontal: 16
|
|
767
904
|
},
|
|
905
|
+
/** Gutter matching the event cards below it, so the pills line up with them. */
|
|
906
|
+
eventFilterBar: {
|
|
907
|
+
paddingHorizontal: 12,
|
|
908
|
+
paddingTop: 8,
|
|
909
|
+
paddingBottom: 4
|
|
910
|
+
},
|
|
768
911
|
emptyState: {
|
|
769
912
|
flex: 1,
|
|
770
913
|
justifyContent: "center",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.StorageScalarKeyEditor = StorageScalarKeyEditor;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactNative = require("react-native");
|
|
9
|
+
var _StorageValueEditModal = require("./StorageValueEditModal");
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
/**
|
|
12
|
+
* Editing a key whose value is a single string, number or boolean.
|
|
13
|
+
*
|
|
14
|
+
* A tree of one row is not a tree, so there's nothing here to select, reorder
|
|
15
|
+
* or append to — which means none of the structural editor applies. And with
|
|
16
|
+
* one edit producing one write, there's nothing to buffer either: a draft would
|
|
17
|
+
* only add a step between typing the value and storing it.
|
|
18
|
+
*
|
|
19
|
+
* So this is just the value modal over whatever the user was looking at, on
|
|
20
|
+
* both the phone and the dashboard. Changing one string shouldn't black out the
|
|
21
|
+
* screen it came from.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
function StorageScalarKeyEditor({
|
|
25
|
+
storageKey,
|
|
26
|
+
value,
|
|
27
|
+
onSave,
|
|
28
|
+
onClose
|
|
29
|
+
}) {
|
|
30
|
+
const [saving, setSaving] = (0, _react.useState)(false);
|
|
31
|
+
const save = (0, _react.useCallback)(next => {
|
|
32
|
+
if (saving) return;
|
|
33
|
+
setSaving(true);
|
|
34
|
+
onSave(JSON.stringify(next)).then(onClose).catch(e => {
|
|
35
|
+
setSaving(false);
|
|
36
|
+
_reactNative.Alert.alert("Couldn't save", e instanceof Error ? e.message : String(e));
|
|
37
|
+
});
|
|
38
|
+
}, [onClose, onSave, saving]);
|
|
39
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_StorageValueEditModal.StorageValueEditModal, {
|
|
40
|
+
path: [storageKey.key],
|
|
41
|
+
value: value,
|
|
42
|
+
onSave: save,
|
|
43
|
+
onCancel: onClose,
|
|
44
|
+
busy: saving
|
|
45
|
+
});
|
|
46
|
+
}
|