@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.
Files changed (180) hide show
  1. package/README.md +11 -90
  2. package/lib/commonjs/index.js +1 -353
  3. package/lib/commonjs/preset.js +1 -85
  4. package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +1 -36
  5. package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -12
  6. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +1 -24
  7. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +3 -1259
  8. package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +1 -257
  9. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +1 -419
  10. package/lib/commonjs/storage/components/SelectionActionBar.js +1 -207
  11. package/lib/commonjs/storage/components/StorageActionButtons.js +1 -242
  12. package/lib/commonjs/storage/components/StorageBrowserMode.js +1 -61
  13. package/lib/commonjs/storage/components/StorageEventActionButton.js +1 -121
  14. package/lib/commonjs/storage/components/StorageEventCard.js +1 -118
  15. package/lib/commonjs/storage/components/StorageEventDetailContent.js +1 -494
  16. package/lib/commonjs/storage/components/StorageEventFilterView.js +1 -160
  17. package/lib/commonjs/storage/components/StorageEventsSection.js +1 -24
  18. package/lib/commonjs/storage/components/StorageFilterCards.js +1 -231
  19. package/lib/commonjs/storage/components/StorageKeyCard.js +1 -520
  20. package/lib/commonjs/storage/components/StorageKeyDetailScreen.js +1 -579
  21. package/lib/commonjs/storage/components/StorageKeyRow.js +1 -559
  22. package/lib/commonjs/storage/components/StorageKeySection.js +1 -160
  23. package/lib/commonjs/storage/components/StorageKeyStats.js +1 -345
  24. package/lib/commonjs/storage/components/StorageModalWithTabs.js +1 -954
  25. package/lib/commonjs/storage/components/StorageSaveNotice.js +1 -171
  26. package/lib/commonjs/storage/components/StorageSection.js +1 -43
  27. package/lib/commonjs/storage/components/StorageTypeFilterBar.js +1 -144
  28. package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +1 -141
  29. package/lib/commonjs/storage/hooks/useMMKVInstances.js +1 -210
  30. package/lib/commonjs/storage/hooks/useMMKVKeys.js +1 -376
  31. package/lib/commonjs/storage/hooks/useSecureStoreKeys.js +1 -202
  32. package/lib/commonjs/storage/hooks/useStorageEvents.js +1 -61
  33. package/lib/commonjs/storage/stores/storageEventStore.js +1 -312
  34. package/lib/commonjs/storage/sync/storageSyncAdapter.js +1 -492
  35. package/lib/commonjs/storage/types.js +1 -5
  36. package/lib/commonjs/storage/utils/AsyncStorageListener.js +1 -691
  37. package/lib/commonjs/storage/utils/AsyncStorageListener.web.js +1 -89
  38. package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +1 -203
  39. package/lib/commonjs/storage/utils/MMKVListener.js +1 -416
  40. package/lib/commonjs/storage/utils/SecureStoreRegistry.js +1 -200
  41. package/lib/commonjs/storage/utils/asyncStorageCompat.js +1 -89
  42. package/lib/commonjs/storage/utils/browserStorageLifecycle.js +1 -10
  43. package/lib/commonjs/storage/utils/browserStorageLifecycle.web.js +1 -70
  44. package/lib/commonjs/storage/utils/clearAllStorage.js +1 -48
  45. package/lib/commonjs/storage/utils/deleteStorageKeys.js +1 -82
  46. package/lib/commonjs/storage/utils/lineDiff.js +1 -30
  47. package/lib/commonjs/storage/utils/mmkvAvailability.js +1 -62
  48. package/lib/commonjs/storage/utils/mmkvAvailability.web.js +1 -21
  49. package/lib/commonjs/storage/utils/mmkvTypeDetection.js +1 -154
  50. package/lib/commonjs/storage/utils/requiredStorageKeyScope.js +1 -17
  51. package/lib/commonjs/storage/utils/setStorageValue.js +1 -218
  52. package/lib/commonjs/storage/utils/snapshotProvider.js +1 -382
  53. package/lib/commonjs/storage/utils/storageActionHelpers.js +1 -46
  54. package/lib/commonjs/storage/utils/storageBrowserRefresh.js +1 -49
  55. package/lib/commonjs/storage/utils/storageLabels.js +1 -13
  56. package/lib/commonjs/storage/utils/storageLabels.web.js +1 -13
  57. package/lib/commonjs/storage/utils/storageOps.js +1 -53
  58. package/lib/commonjs/storage/utils/storageQueryUtils.js +1 -35
  59. package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +1 -288
  60. package/lib/commonjs/storage/utils/valueType.js +1 -83
  61. package/lib/module/index.js +1 -114
  62. package/lib/module/preset.js +1 -82
  63. package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +1 -32
  64. package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +1 -9
  65. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +1 -10
  66. package/lib/module/storage/components/GameUIStorageBrowser.js +3 -1257
  67. package/lib/module/storage/components/MMKVInstanceInfoPanel.js +1 -253
  68. package/lib/module/storage/components/MMKVInstanceSelector.js +1 -415
  69. package/lib/module/storage/components/SelectionActionBar.js +1 -204
  70. package/lib/module/storage/components/StorageActionButtons.js +1 -238
  71. package/lib/module/storage/components/StorageBrowserMode.js +1 -57
  72. package/lib/module/storage/components/StorageEventActionButton.js +1 -118
  73. package/lib/module/storage/components/StorageEventCard.js +1 -113
  74. package/lib/module/storage/components/StorageEventDetailContent.js +1 -490
  75. package/lib/module/storage/components/StorageEventFilterView.js +1 -156
  76. package/lib/module/storage/components/StorageEventsSection.js +1 -20
  77. package/lib/module/storage/components/StorageFilterCards.js +1 -227
  78. package/lib/module/storage/components/StorageKeyCard.js +1 -517
  79. package/lib/module/storage/components/StorageKeyDetailScreen.js +1 -577
  80. package/lib/module/storage/components/StorageKeyRow.js +1 -554
  81. package/lib/module/storage/components/StorageKeySection.js +1 -155
  82. package/lib/module/storage/components/StorageKeyStats.js +1 -342
  83. package/lib/module/storage/components/StorageModalWithTabs.js +1 -949
  84. package/lib/module/storage/components/StorageSaveNotice.js +1 -168
  85. package/lib/module/storage/components/StorageSection.js +1 -40
  86. package/lib/module/storage/components/StorageTypeFilterBar.js +1 -139
  87. package/lib/module/storage/hooks/useAsyncStorageKeys.js +1 -136
  88. package/lib/module/storage/hooks/useMMKVInstances.js +1 -204
  89. package/lib/module/storage/hooks/useMMKVKeys.js +1 -371
  90. package/lib/module/storage/hooks/useSecureStoreKeys.js +1 -199
  91. package/lib/module/storage/hooks/useStorageEvents.js +1 -58
  92. package/lib/module/storage/stores/storageEventStore.js +1 -301
  93. package/lib/module/storage/sync/storageSyncAdapter.js +1 -486
  94. package/lib/module/storage/types.js +1 -3
  95. package/lib/module/storage/utils/AsyncStorageListener.js +1 -679
  96. package/lib/module/storage/utils/AsyncStorageListener.web.js +1 -76
  97. package/lib/module/storage/utils/MMKVInstanceRegistry.js +1 -197
  98. package/lib/module/storage/utils/MMKVListener.js +1 -403
  99. package/lib/module/storage/utils/SecureStoreRegistry.js +1 -194
  100. package/lib/module/storage/utils/asyncStorageCompat.js +1 -81
  101. package/lib/module/storage/utils/browserStorageLifecycle.js +1 -6
  102. package/lib/module/storage/utils/browserStorageLifecycle.web.js +1 -66
  103. package/lib/module/storage/utils/clearAllStorage.js +1 -43
  104. package/lib/module/storage/utils/deleteStorageKeys.js +1 -78
  105. package/lib/module/storage/utils/lineDiff.js +1 -12
  106. package/lib/module/storage/utils/mmkvAvailability.js +1 -56
  107. package/lib/module/storage/utils/mmkvAvailability.web.js +1 -15
  108. package/lib/module/storage/utils/mmkvTypeDetection.js +1 -148
  109. package/lib/module/storage/utils/requiredStorageKeyScope.js +1 -13
  110. package/lib/module/storage/utils/setStorageValue.js +1 -209
  111. package/lib/module/storage/utils/snapshotProvider.js +1 -380
  112. package/lib/module/storage/utils/storageActionHelpers.js +1 -42
  113. package/lib/module/storage/utils/storageBrowserRefresh.js +1 -44
  114. package/lib/module/storage/utils/storageLabels.js +1 -9
  115. package/lib/module/storage/utils/storageLabels.web.js +1 -9
  116. package/lib/module/storage/utils/storageOps.js +1 -46
  117. package/lib/module/storage/utils/storageQueryUtils.js +1 -30
  118. package/lib/module/storage/utils/storageTimeTravelUtils.js +1 -281
  119. package/lib/module/storage/utils/valueType.js +1 -76
  120. package/lib/web/index.mjs +2 -3
  121. package/package.json +6 -10
  122. package/lib/typescript/index.d.ts.map +0 -1
  123. package/lib/typescript/preset.d.ts.map +0 -1
  124. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +0 -1
  125. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +0 -1
  126. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +0 -1
  127. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +0 -1
  128. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +0 -1
  129. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +0 -1
  130. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +0 -1
  131. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +0 -1
  132. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +0 -1
  133. package/lib/typescript/storage/components/StorageEventActionButton.d.ts.map +0 -1
  134. package/lib/typescript/storage/components/StorageEventCard.d.ts.map +0 -1
  135. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +0 -1
  136. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +0 -1
  137. package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +0 -1
  138. package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +0 -1
  139. package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +0 -1
  140. package/lib/typescript/storage/components/StorageKeyDetailScreen.d.ts.map +0 -1
  141. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +0 -1
  142. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +0 -1
  143. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +0 -1
  144. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +0 -1
  145. package/lib/typescript/storage/components/StorageSaveNotice.d.ts.map +0 -1
  146. package/lib/typescript/storage/components/StorageSection.d.ts.map +0 -1
  147. package/lib/typescript/storage/components/StorageTypeFilterBar.d.ts.map +0 -1
  148. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +0 -1
  149. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +0 -1
  150. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +0 -1
  151. package/lib/typescript/storage/hooks/useSecureStoreKeys.d.ts.map +0 -1
  152. package/lib/typescript/storage/hooks/useStorageEvents.d.ts.map +0 -1
  153. package/lib/typescript/storage/stores/storageEventStore.d.ts.map +0 -1
  154. package/lib/typescript/storage/sync/storageSyncAdapter.d.ts.map +0 -1
  155. package/lib/typescript/storage/types.d.ts.map +0 -1
  156. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +0 -1
  157. package/lib/typescript/storage/utils/AsyncStorageListener.web.d.ts.map +0 -1
  158. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +0 -1
  159. package/lib/typescript/storage/utils/MMKVListener.d.ts.map +0 -1
  160. package/lib/typescript/storage/utils/SecureStoreRegistry.d.ts.map +0 -1
  161. package/lib/typescript/storage/utils/asyncStorageCompat.d.ts.map +0 -1
  162. package/lib/typescript/storage/utils/browserStorageLifecycle.d.ts.map +0 -1
  163. package/lib/typescript/storage/utils/browserStorageLifecycle.web.d.ts.map +0 -1
  164. package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +0 -1
  165. package/lib/typescript/storage/utils/deleteStorageKeys.d.ts.map +0 -1
  166. package/lib/typescript/storage/utils/lineDiff.d.ts.map +0 -1
  167. package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +0 -1
  168. package/lib/typescript/storage/utils/mmkvAvailability.web.d.ts.map +0 -1
  169. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +0 -1
  170. package/lib/typescript/storage/utils/requiredStorageKeyScope.d.ts.map +0 -1
  171. package/lib/typescript/storage/utils/setStorageValue.d.ts.map +0 -1
  172. package/lib/typescript/storage/utils/snapshotProvider.d.ts.map +0 -1
  173. package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +0 -1
  174. package/lib/typescript/storage/utils/storageBrowserRefresh.d.ts.map +0 -1
  175. package/lib/typescript/storage/utils/storageLabels.d.ts.map +0 -1
  176. package/lib/typescript/storage/utils/storageLabels.web.d.ts.map +0 -1
  177. package/lib/typescript/storage/utils/storageOps.d.ts.map +0 -1
  178. package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +0 -1
  179. package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +0 -1
  180. 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}});