@buoy-gg/storage 1.7.5 → 1.7.8

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 (40) hide show
  1. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +35 -44
  2. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +9 -23
  3. package/lib/commonjs/storage/components/SelectionActionBar.js +8 -22
  4. package/lib/commonjs/storage/components/StorageActionButtons.js +8 -22
  5. package/lib/commonjs/storage/components/StorageActions.js +8 -22
  6. package/lib/commonjs/storage/components/StorageEventActionButton.js +120 -0
  7. package/lib/commonjs/storage/components/StorageEventDetailContent.js +331 -822
  8. package/lib/commonjs/storage/components/StorageModalWithTabs.js +2 -23
  9. package/lib/commonjs/storage/utils/AsyncStorageListener.js +164 -35
  10. package/lib/commonjs/storage/utils/index.js +37 -0
  11. package/lib/commonjs/storage/utils/storageTimeTravelUtils.js +251 -0
  12. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +35 -44
  13. package/lib/module/storage/components/GameUIStorageBrowser.js +9 -23
  14. package/lib/module/storage/components/SelectionActionBar.js +9 -24
  15. package/lib/module/storage/components/StorageActionButtons.js +9 -24
  16. package/lib/module/storage/components/StorageActions.js +9 -24
  17. package/lib/module/storage/components/StorageEventActionButton.js +117 -0
  18. package/lib/module/storage/components/StorageEventDetailContent.js +332 -824
  19. package/lib/module/storage/components/StorageModalWithTabs.js +2 -22
  20. package/lib/module/storage/utils/AsyncStorageListener.js +159 -33
  21. package/lib/module/storage/utils/index.js +4 -1
  22. package/lib/module/storage/utils/storageTimeTravelUtils.js +245 -0
  23. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts +1 -1
  24. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +1 -1
  25. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -1
  26. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -1
  27. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -1
  28. package/lib/typescript/storage/components/StorageActions.d.ts.map +1 -1
  29. package/lib/typescript/storage/components/StorageEventActionButton.d.ts +37 -0
  30. package/lib/typescript/storage/components/StorageEventActionButton.d.ts.map +1 -0
  31. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +11 -3
  32. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -1
  33. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -1
  34. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +38 -1
  35. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -1
  36. package/lib/typescript/storage/utils/index.d.ts +2 -1
  37. package/lib/typescript/storage/utils/index.d.ts.map +1 -1
  38. package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts +35 -0
  39. package/lib/typescript/storage/utils/storageTimeTravelUtils.d.ts.map +1 -0
  40. package/package.json +6 -4
@@ -1,380 +1,278 @@
1
1
  "use strict";
2
2
 
3
- import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from "react-native";
4
- import { useEffect, useState, useCallback, useRef } from "react";
3
+ /**
4
+ * StorageEventDetailContent
5
+ *
6
+ * Detail view for storage events using the shared EventHistoryViewer component.
7
+ */
8
+
9
+ import { View, Text, StyleSheet, ScrollView } from "react-native";
10
+ import { useState, useCallback, useMemo, useEffect, useRef } from "react";
5
11
  import AsyncStorage from "@react-native-async-storage/async-storage";
6
- import { formatRelativeTime, macOSColors, parseValue, devToolsStorageKeys, ChevronLeft, ChevronRight, AlertCircle, X, Database, GitBranch, EventStepperFooter, Lock } from "@buoy-gg/shared-ui";
12
+ import { formatRelativeTime, macOSColors, buoyColors, parseValue, devToolsStorageKeys, Database, GitBranch, EventStepperFooter, EventHistoryViewer, copyToClipboard, ProUpgradeModal } from "@buoy-gg/shared-ui";
13
+ import { useIsPro } from "@buoy-gg/license";
7
14
  import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
8
15
  import { ThemedSplitView } from "./DiffViewer/modes/ThemedSplitView";
9
16
  import { diffThemes } from "./DiffViewer/themes/diffThemes";
10
- import { computeLineDiff, DiffType } from "../utils/lineDiff";
11
17
  import { TreeDiffViewer } from "./DiffViewer/TreeDiffViewer";
12
18
  import { translateStorageAction } from "../utils/storageActionHelpers";
19
+ import { StorageEventActionButton } from "./StorageEventActionButton";
20
+ import { undoOperation, jumpToState, canUndo } from "../utils/storageTimeTravelUtils";
13
21
 
14
- // Lazy load the license hooks to avoid circular dependencies
22
+ // Unified storage event type (supports both AsyncStorage and MMKV)
15
23
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
16
- let _useIsPro = null;
17
- let _licenseLoadAttempted = false;
18
- function loadLicenseModule() {
19
- if (_licenseLoadAttempted) return;
20
- _licenseLoadAttempted = true;
21
- try {
22
- const mod = require("@buoy-gg/license");
23
- if (mod) {
24
- _useIsPro = mod.useIsPro ?? null;
25
- }
26
- } catch {
27
- // License package not available
24
+ /**
25
+ * Get color for storage action
26
+ */
27
+ function getActionColor(action) {
28
+ switch (action) {
29
+ case "setItem":
30
+ case "multiSet":
31
+ return macOSColors.semantic.success;
32
+ case "removeItem":
33
+ case "multiRemove":
34
+ case "clear":
35
+ return macOSColors.semantic.error;
36
+ case "mergeItem":
37
+ case "multiMerge":
38
+ return macOSColors.semantic.info;
39
+ default:
40
+ return macOSColors.text.muted;
28
41
  }
29
42
  }
30
- function getUseIsPro() {
31
- loadLicenseModule();
32
- return _useIsPro ?? (() => false);
33
- }
34
-
35
- // Free tier limit for event history navigation
36
- const FREE_TIER_EVENT_HISTORY_LIMIT = 3;
37
43
 
38
- // Unified storage event type (supports both AsyncStorage and MMKV)
44
+ /**
45
+ * Get the type of a value
46
+ */
47
+ function getValueType(value) {
48
+ if (value === null) return "null";
49
+ if (value === undefined) return "undefined";
50
+ if (Array.isArray(value)) return "array";
51
+ return typeof value;
52
+ }
39
53
 
54
+ /**
55
+ * Format timestamp with milliseconds
56
+ */
57
+ function formatTimeWithMs(date) {
58
+ const h = String(date.getHours()).padStart(2, "0");
59
+ const m = String(date.getMinutes()).padStart(2, "0");
60
+ const s = String(date.getSeconds()).padStart(2, "0");
61
+ const ms = String(date.getMilliseconds()).padStart(3, "0");
62
+ return `${h}:${m}:${s}.${ms}`;
63
+ }
40
64
  export function StorageEventDetailContent({
41
65
  conversation,
42
66
  selectedEventIndex = 0,
43
67
  onEventIndexChange = () => {},
44
68
  disableInternalFooter = false
45
69
  }) {
46
- // Check Pro status internally
47
- const useIsPro = getUseIsPro();
70
+ // Pro feature gating
48
71
  const isPro = useIsPro();
49
- // Internal view state - now managed internally instead of via props
50
- const [internalActiveView, setInternalActiveView] = useState("current");
51
- // Compare-any-two state for Diff tab
72
+ const [showProModal, setShowProModal] = useState(false);
73
+
74
+ // View state
75
+ const [activeView, setActiveView] = useState("current");
76
+ const [diffMode, setDiffMode] = useState("tree");
77
+
78
+ // Compare indices for diff view
52
79
  const [leftIndex, setLeftIndex] = useState(Math.max(0, selectedEventIndex - 1));
53
80
  const [rightIndex, setRightIndex] = useState(selectedEventIndex);
54
- const [isLeftPickerOpen, setIsLeftPickerOpen] = useState(false);
55
- const [isRightPickerOpen, setIsRightPickerOpen] = useState(false);
56
- const [diffViewerTab, setDiffViewerTab] = useState("tree");
57
81
 
58
82
  // Track if preferences have been loaded
59
83
  const hasLoadedPreferences = useRef(false);
60
84
 
85
+ // Get sorted events
86
+ const navigationItems = useMemo(() => conversation.events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()), [conversation.events]);
87
+ const totalEvents = navigationItems.length;
88
+
61
89
  // Load saved preferences on mount
62
90
  useEffect(() => {
63
91
  if (hasLoadedPreferences.current) return;
64
92
  const loadPreferences = async () => {
65
93
  try {
66
- // Load detail view preference (current/diff)
67
94
  const savedDetailView = await AsyncStorage.getItem(devToolsStorageKeys.storage.detailView());
68
95
  if (savedDetailView === "current" || savedDetailView === "diff") {
69
- setInternalActiveView(savedDetailView);
96
+ setActiveView(savedDetailView);
70
97
  }
71
-
72
- // Load diff viewer mode preference (split/tree)
73
98
  const savedDiffMode = await AsyncStorage.getItem(devToolsStorageKeys.storage.diffViewerMode());
74
99
  if (savedDiffMode === "split" || savedDiffMode === "tree") {
75
- setDiffViewerTab(savedDiffMode);
100
+ setDiffMode(savedDiffMode);
76
101
  }
77
102
  hasLoadedPreferences.current = true;
78
- } catch (error) {
79
- // Failed to load view preferences
103
+ } catch {
104
+ // Failed to load preferences
80
105
  }
81
106
  };
82
107
  loadPreferences();
83
108
  }, []);
84
109
 
85
- // Save detail view preference when changed
110
+ // Keep compare indices synced to selection
111
+ useEffect(() => {
112
+ const newRight = Math.min(totalEvents - 1, Math.max(0, selectedEventIndex));
113
+ const newLeft = Math.max(0, Math.min(newRight - 1, selectedEventIndex - 1));
114
+ setLeftIndex(newLeft);
115
+ setRightIndex(newRight);
116
+ }, [selectedEventIndex, totalEvents]);
117
+
118
+ // Save view preference when changed
86
119
  const handleViewChange = useCallback(async view => {
87
- setInternalActiveView(view);
120
+ setActiveView(view);
88
121
  try {
89
122
  await AsyncStorage.setItem(devToolsStorageKeys.storage.detailView(), view);
90
- } catch (error) {
91
- // Failed to save detail view preference
123
+ } catch {
124
+ // Failed to save preference
92
125
  }
93
126
  }, []);
94
127
 
95
- // Save diff viewer mode preference when changed
128
+ // Save diff mode preference when changed
96
129
  const handleDiffModeChange = useCallback(async mode => {
97
- setDiffViewerTab(mode);
130
+ setDiffMode(mode);
98
131
  try {
99
132
  await AsyncStorage.setItem(devToolsStorageKeys.storage.diffViewerMode(), mode);
100
- } catch (error) {
101
- // Failed to save diff viewer mode preference
133
+ } catch {
134
+ // Failed to save preference
102
135
  }
103
136
  }, []);
104
- const getActionColor = action => {
105
- switch (action) {
106
- case "setItem":
107
- case "multiSet":
108
- return macOSColors.semantic.success;
109
- case "removeItem":
110
- case "multiRemove":
111
- case "clear":
112
- return macOSColors.semantic.error;
113
- case "mergeItem":
114
- case "multiMerge":
115
- return macOSColors.semantic.info;
116
- default:
117
- return macOSColors.text.muted;
137
+
138
+ // Get current event
139
+ const selectedEvent = navigationItems[selectedEventIndex];
140
+ const valueToShow = selectedEvent?.data?.value ?? conversation.currentValue;
141
+ const action = selectedEvent?.action;
142
+ const valueType = getValueType(valueToShow);
143
+ const actionColor = getActionColor(action || "");
144
+
145
+ // Get events for diff comparison
146
+ const leftEvent = navigationItems[Math.max(0, Math.min(totalEvents - 1, leftIndex))];
147
+ const rightEvent = navigationItems[Math.max(0, Math.min(totalEvents - 1, rightIndex))];
148
+
149
+ // Create event display info for compare bar
150
+ const leftEventInfo = useMemo(() => ({
151
+ index: leftIndex,
152
+ label: `#${leftIndex + 1} / ${totalEvents}`,
153
+ timestamp: leftEvent ? formatTimeWithMs(leftEvent.timestamp) : "",
154
+ relativeTime: leftEvent ? formatRelativeTime(leftEvent.timestamp) : "",
155
+ badge: leftEvent ? /*#__PURE__*/_jsx(View, {
156
+ style: [styles.actionBadgeSmall, {
157
+ backgroundColor: `${getActionColor(leftEvent.action)}20`
158
+ }],
159
+ children: /*#__PURE__*/_jsx(Text, {
160
+ style: [styles.actionTextSmall, {
161
+ color: getActionColor(leftEvent.action)
162
+ }],
163
+ children: translateStorageAction(leftEvent.action)
164
+ })
165
+ }) : undefined
166
+ }), [leftIndex, leftEvent, totalEvents]);
167
+ const rightEventInfo = useMemo(() => ({
168
+ index: rightIndex,
169
+ label: `#${rightIndex + 1} / ${totalEvents}`,
170
+ timestamp: rightEvent ? formatTimeWithMs(rightEvent.timestamp) : "",
171
+ relativeTime: rightEvent ? formatRelativeTime(rightEvent.timestamp) : "",
172
+ badge: rightEvent ? /*#__PURE__*/_jsx(View, {
173
+ style: [styles.actionBadgeSmall, {
174
+ backgroundColor: `${getActionColor(rightEvent.action)}20`
175
+ }],
176
+ children: /*#__PURE__*/_jsx(Text, {
177
+ style: [styles.actionTextSmall, {
178
+ color: getActionColor(rightEvent.action)
179
+ }],
180
+ children: translateStorageAction(rightEvent.action)
181
+ })
182
+ }) : undefined
183
+ }), [rightIndex, rightEvent, totalEvents]);
184
+
185
+ // Diff mode tabs
186
+ const diffModeTabs = useMemo(() => [{
187
+ key: "tree",
188
+ label: "TREE VIEW"
189
+ }, {
190
+ key: "split",
191
+ label: "SPLIT VIEW"
192
+ }], []);
193
+
194
+ // Navigation handlers for compare bar
195
+ const handleLeftPrevious = useCallback(() => {
196
+ if (leftIndex > 0) {
197
+ setLeftIndex(leftIndex - 1);
198
+ }
199
+ }, [leftIndex]);
200
+ const handleLeftNext = useCallback(() => {
201
+ if (leftIndex < rightIndex - 1) {
202
+ setLeftIndex(leftIndex + 1);
203
+ }
204
+ }, [leftIndex, rightIndex]);
205
+ const handleRightPrevious = useCallback(() => {
206
+ if (rightIndex > leftIndex + 1) {
207
+ setRightIndex(rightIndex - 1);
208
+ }
209
+ }, [rightIndex, leftIndex]);
210
+ const handleRightNext = useCallback(() => {
211
+ if (rightIndex < totalEvents - 1) {
212
+ setRightIndex(rightIndex + 1);
118
213
  }
119
- };
120
- const renderValueContent = (value, label, action) => {
121
- const parsed = parseValue(value);
122
- const type = parsed === null ? "null" : parsed === undefined ? "undefined" : Array.isArray(parsed) ? "array" : typeof parsed;
214
+ }, [rightIndex, totalEvents]);
215
+
216
+ // Render current value view
217
+ const renderCurrentView = useCallback(() => {
218
+ const parsed = parseValue(valueToShow);
123
219
  return /*#__PURE__*/_jsxs(View, {
124
- style: styles.valueContent,
220
+ style: styles.contentCard,
125
221
  children: [/*#__PURE__*/_jsxs(View, {
126
222
  style: styles.valueHeader,
127
223
  children: [/*#__PURE__*/_jsx(Text, {
128
224
  style: styles.valueLabel,
129
- children: label
225
+ children: "CURRENT VALUE"
130
226
  }), /*#__PURE__*/_jsxs(View, {
131
227
  style: styles.valueHeaderBadges,
132
228
  children: [action && /*#__PURE__*/_jsx(View, {
133
229
  style: [styles.actionBadge, {
134
- backgroundColor: `${getActionColor(action)}20`
230
+ backgroundColor: `${actionColor}20`
135
231
  }],
136
232
  children: /*#__PURE__*/_jsx(Text, {
137
233
  style: [styles.actionText, {
138
- color: getActionColor(action)
234
+ color: actionColor
139
235
  }],
140
236
  children: translateStorageAction(action)
141
237
  })
142
238
  }), /*#__PURE__*/_jsx(View, {
143
- style: [styles.typeBadge],
239
+ style: styles.typeBadge,
144
240
  children: /*#__PURE__*/_jsx(Text, {
145
241
  style: styles.typeText,
146
- children: type.toUpperCase()
242
+ children: valueType.toUpperCase()
147
243
  })
148
244
  })]
149
245
  })]
150
246
  }), /*#__PURE__*/_jsx(View, {
151
247
  style: styles.valueBox,
152
- children: type === "object" || type === "array" ? parsed && (Array.isArray(parsed) && parsed.length > 0 || typeof parsed === "object" && Object.keys(parsed).length > 0) ? /*#__PURE__*/_jsx(DataViewer, {
248
+ children: (valueType === "object" || valueType === "array") && parsed ? Array.isArray(parsed) && parsed.length > 0 || typeof parsed === "object" && Object.keys(parsed).length > 0 ? /*#__PURE__*/_jsx(DataViewer, {
153
249
  title: "",
154
250
  data: parsed,
155
251
  showTypeFilter: false
156
252
  }) : /*#__PURE__*/_jsx(Text, {
157
253
  style: styles.valueText,
158
- children: type === "array" ? "[]" : "{}"
254
+ children: valueType === "array" ? "[]" : "{}"
159
255
  }) : /*#__PURE__*/_jsx(Text, {
160
256
  style: styles.valueText,
161
- children: parsed === null ? "null" : parsed === undefined ? "undefined" : type === "string" ? `"${parsed}"` : String(parsed)
257
+ children: parsed === null ? "null" : parsed === undefined ? "undefined" : valueType === "string" ? `"${parsed}"` : String(parsed)
162
258
  })
163
259
  })]
164
260
  });
165
- };
166
-
167
- // Get all events sorted by time
168
- const navigationItems = conversation.events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
169
- const totalEvents = navigationItems.length;
170
-
171
- // Free tier limit - max event index accessible
172
- const maxAccessibleEventIndex = isPro ? totalEvents - 1 : Math.min(FREE_TIER_EVENT_HISTORY_LIMIT - 1, totalEvents - 1);
173
- const lockedEventCount = isPro ? 0 : Math.max(0, totalEvents - FREE_TIER_EVENT_HISTORY_LIMIT);
174
-
175
- // Keep compare indices synced to selection
176
- useEffect(() => {
177
- const newRight = Math.min(totalEvents - 1, Math.max(0, selectedEventIndex));
178
- const newLeft = Math.max(0, Math.min(newRight - 1, selectedEventIndex - 1));
179
- setLeftIndex(newLeft);
180
- setRightIndex(newRight);
181
- }, [selectedEventIndex, totalEvents]);
182
-
183
- // Precise time HH:MM:SS.mmm
184
- const formatTimeWithMs = useCallback(date => {
185
- const h = String(date.getHours()).padStart(2, "0");
186
- const m = String(date.getMinutes()).padStart(2, "0");
187
- const s = String(date.getSeconds()).padStart(2, "0");
188
- const ms = String(date.getMilliseconds()).padStart(3, "0");
189
- return `${h}:${m}:${s}.${ms}`;
190
- }, []);
191
- const bumpLeft = delta => {
192
- if (totalEvents < 2) return;
193
- const maxLeft = isPro ? totalEvents - 2 : Math.min(maxAccessibleEventIndex - 1, totalEvents - 2);
194
- let next = Math.max(0, Math.min(maxLeft, leftIndex + delta));
195
- if (next >= rightIndex) next = Math.max(0, rightIndex - 1);
196
- setLeftIndex(next);
197
- };
198
- const bumpRight = delta => {
199
- if (totalEvents < 2) return;
200
- const maxRight = isPro ? totalEvents - 1 : maxAccessibleEventIndex;
201
- let next = Math.max(1, Math.min(maxRight, rightIndex + delta));
202
- if (next <= leftIndex) next = Math.min(maxRight, leftIndex + 1);
203
- setRightIndex(next);
204
- };
205
-
206
- // Render current value tab
207
- const renderCurrentValue = () => {
208
- const selectedEvent = navigationItems[selectedEventIndex];
209
- const valueToShow = selectedEvent?.data?.value ?? conversation.currentValue;
210
- const action = selectedEvent?.action;
211
- return /*#__PURE__*/_jsx(View, {
212
- style: styles.fullPageSection,
213
- children: /*#__PURE__*/_jsx(View, {
214
- style: styles.contentCard,
215
- children: renderValueContent(valueToShow, "CURRENT VALUE", action)
216
- })
217
- });
218
- };
261
+ }, [valueToShow, action, actionColor, valueType]);
219
262
 
220
- // Render diff tab
221
- const renderDiff = () => {
222
- if (navigationItems.length === 0) {
223
- return /*#__PURE__*/_jsxs(View, {
224
- style: styles.emptyState,
225
- children: [/*#__PURE__*/_jsx(AlertCircle, {
226
- size: 32,
227
- color: macOSColors.text.muted
228
- }), /*#__PURE__*/_jsx(Text, {
229
- style: styles.emptyText,
230
- children: "No changes to display"
231
- })]
232
- });
233
- }
234
- const leftEvent = navigationItems[Math.max(0, Math.min(totalEvents - 1, leftIndex))];
235
- const rightEvent = navigationItems[Math.max(0, Math.min(totalEvents - 1, rightIndex))];
236
- const previousValue = leftEvent?.data?.value ?? null;
237
- const currentValue = rightEvent?.data?.value;
238
- return /*#__PURE__*/_jsxs(View, {
239
- style: styles.fullPageSection,
240
- children: [/*#__PURE__*/_jsxs(View, {
241
- style: styles.diffViewerTabs,
242
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
243
- style: [styles.diffViewerTab, diffViewerTab === "split" && styles.diffViewerTabActive],
244
- onPress: () => handleDiffModeChange("split"),
245
- children: /*#__PURE__*/_jsx(Text, {
246
- style: [styles.diffViewerTabText, diffViewerTab === "split" && styles.diffViewerTabTextActive],
247
- children: "SPLIT VIEW"
248
- })
249
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
250
- style: [styles.diffViewerTab, diffViewerTab === "tree" && styles.diffViewerTabActive],
251
- onPress: () => handleDiffModeChange("tree"),
252
- children: /*#__PURE__*/_jsx(Text, {
253
- style: [styles.diffViewerTabText, diffViewerTab === "tree" && styles.diffViewerTabTextActive],
254
- children: "TREE VIEW"
255
- })
256
- })]
257
- }), totalEvents > 0 && /*#__PURE__*/_jsxs(View, {
258
- style: styles.compareBar,
259
- children: [/*#__PURE__*/_jsxs(View, {
260
- style: styles.compareSide,
261
- children: [/*#__PURE__*/_jsxs(View, {
262
- style: styles.compareLabelRow,
263
- children: [/*#__PURE__*/_jsx(Text, {
264
- style: [styles.compareLabel, {
265
- color: macOSColors.semantic.debug
266
- }],
267
- children: "PREV"
268
- }), /*#__PURE__*/_jsx(View, {
269
- style: [styles.compareActionBadge, {
270
- backgroundColor: `${getActionColor(leftEvent.action)}20`
271
- }],
272
- children: /*#__PURE__*/_jsx(Text, {
273
- style: [styles.compareActionText, {
274
- color: getActionColor(leftEvent.action)
275
- }],
276
- children: translateStorageAction(leftEvent.action)
277
- })
278
- })]
279
- }), /*#__PURE__*/_jsxs(View, {
280
- style: styles.compareControls,
281
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
282
- onPress: () => bumpLeft(-1),
283
- disabled: leftIndex <= 0,
284
- style: [styles.compareBtn, leftIndex <= 0 && styles.compareBtnDisabled],
285
- children: /*#__PURE__*/_jsx(ChevronLeft, {
286
- size: 14,
287
- color: leftIndex <= 0 ? macOSColors.text.muted : macOSColors.text.secondary
288
- })
289
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
290
- style: styles.compareMeta,
291
- onPress: () => setIsLeftPickerOpen(true),
292
- activeOpacity: 0.8,
293
- children: [/*#__PURE__*/_jsxs(Text, {
294
- style: styles.compareIndex,
295
- children: ["#", leftIndex + 1, " / ", totalEvents]
296
- }), /*#__PURE__*/_jsx(Text, {
297
- style: styles.compareTime,
298
- children: formatTimeWithMs(leftEvent.timestamp)
299
- }), /*#__PURE__*/_jsxs(Text, {
300
- style: styles.compareRelative,
301
- children: ["(", formatRelativeTime(leftEvent.timestamp), ")"]
302
- })]
303
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
304
- onPress: () => bumpLeft(1),
305
- disabled: leftIndex >= rightIndex - 1,
306
- style: [styles.compareBtn, leftIndex >= rightIndex - 1 && styles.compareBtnDisabled],
307
- children: /*#__PURE__*/_jsx(ChevronRight, {
308
- size: 14,
309
- color: leftIndex >= rightIndex - 1 ? macOSColors.text.muted : macOSColors.text.secondary
310
- })
311
- })]
312
- })]
313
- }), /*#__PURE__*/_jsx(View, {
314
- style: styles.compareDivider
315
- }), /*#__PURE__*/_jsxs(View, {
316
- style: styles.compareSide,
317
- children: [/*#__PURE__*/_jsxs(View, {
318
- style: styles.compareLabelRow,
319
- children: [/*#__PURE__*/_jsx(Text, {
320
- style: [styles.compareLabel, {
321
- color: macOSColors.semantic.success
322
- }],
323
- children: "CUR"
324
- }), /*#__PURE__*/_jsx(View, {
325
- style: [styles.compareActionBadge, {
326
- backgroundColor: `${getActionColor(rightEvent.action)}20`
327
- }],
328
- children: /*#__PURE__*/_jsx(Text, {
329
- style: [styles.compareActionText, {
330
- color: getActionColor(rightEvent.action)
331
- }],
332
- children: translateStorageAction(rightEvent.action)
333
- })
334
- })]
335
- }), /*#__PURE__*/_jsxs(View, {
336
- style: styles.compareControls,
337
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
338
- onPress: () => bumpRight(-1),
339
- disabled: rightIndex <= leftIndex + 1,
340
- style: [styles.compareBtn, rightIndex <= leftIndex + 1 && styles.compareBtnDisabled],
341
- children: /*#__PURE__*/_jsx(ChevronLeft, {
342
- size: 14,
343
- color: rightIndex <= leftIndex + 1 ? macOSColors.text.muted : macOSColors.text.secondary
344
- })
345
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
346
- style: styles.compareMeta,
347
- onPress: () => setIsRightPickerOpen(true),
348
- activeOpacity: 0.8,
349
- children: [/*#__PURE__*/_jsxs(Text, {
350
- style: styles.compareIndex,
351
- children: ["#", rightIndex + 1, " / ", totalEvents]
352
- }), /*#__PURE__*/_jsx(Text, {
353
- style: styles.compareTime,
354
- children: formatTimeWithMs(rightEvent.timestamp)
355
- }), /*#__PURE__*/_jsxs(Text, {
356
- style: styles.compareRelative,
357
- children: ["(", formatRelativeTime(rightEvent.timestamp), ")"]
358
- })]
359
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
360
- onPress: () => bumpRight(1),
361
- disabled: rightIndex >= totalEvents - 1,
362
- style: [styles.compareBtn, rightIndex >= totalEvents - 1 && styles.compareBtnDisabled],
363
- children: /*#__PURE__*/_jsx(ChevronRight, {
364
- size: 14,
365
- color: rightIndex >= totalEvents - 1 ? macOSColors.text.muted : macOSColors.text.secondary
366
- })
367
- })]
368
- })]
369
- })]
370
- }), diffViewerTab === "split" && /*#__PURE__*/_jsx(ScrollView, {
263
+ // Render diff content
264
+ const renderDiffContent = useCallback(() => {
265
+ const previousValue = parseValue(leftEvent?.data?.value ?? null);
266
+ const currentValue = parseValue(rightEvent?.data?.value);
267
+ if (diffMode === "split") {
268
+ return /*#__PURE__*/_jsx(ScrollView, {
371
269
  style: {
372
270
  flex: 1
373
271
  },
374
272
  showsVerticalScrollIndicator: true,
375
273
  children: /*#__PURE__*/_jsx(ThemedSplitView, {
376
- oldValue: parseValue(previousValue),
377
- newValue: parseValue(currentValue),
274
+ oldValue: previousValue,
275
+ newValue: currentValue,
378
276
  differences: [],
379
277
  theme: diffThemes.devToolsDefault,
380
278
  options: {
@@ -387,245 +285,185 @@ export function StorageEventDetailContent({
387
285
  },
388
286
  showThemeName: false
389
287
  })
390
- }), diffViewerTab === "tree" && /*#__PURE__*/_jsx(TreeDiffViewer, {
391
- oldValue: parseValue(previousValue),
392
- newValue: parseValue(currentValue)
393
- })]
288
+ });
289
+ }
290
+ return /*#__PURE__*/_jsx(TreeDiffViewer, {
291
+ oldValue: previousValue,
292
+ newValue: currentValue
394
293
  });
395
- };
294
+ }, [leftEvent, rightEvent, diffMode]);
295
+
296
+ // Footer navigation handlers
297
+ const handleFooterPrevious = useCallback(() => {
298
+ onEventIndexChange(Math.max(0, selectedEventIndex - 1));
299
+ }, [selectedEventIndex, onEventIndexChange]);
300
+ const handleFooterNext = useCallback(() => {
301
+ onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1));
302
+ }, [selectedEventIndex, totalEvents, onEventIndexChange]);
303
+
304
+ // Time-travel action handlers
305
+ const handleUndo = useCallback(async () => {
306
+ if (!isPro) {
307
+ setShowProModal(true);
308
+ return;
309
+ }
310
+ if (!selectedEvent || selectedEvent.storageType === "mmkv") return;
311
+ try {
312
+ await undoOperation(selectedEvent);
313
+ } catch {
314
+ // Silent fail
315
+ }
316
+ }, [selectedEvent, isPro]);
317
+ const handleJump = useCallback(async () => {
318
+ if (!isPro) {
319
+ setShowProModal(true);
320
+ return;
321
+ }
322
+ if (!selectedEvent || selectedEvent.storageType === "mmkv") return;
323
+ const asyncEvents = navigationItems.filter(e => e.storageType !== "mmkv");
324
+ const targetIndex = asyncEvents.findIndex(e => e.timestamp.getTime() === selectedEvent.timestamp.getTime());
325
+ if (targetIndex === -1) return;
326
+ try {
327
+ await jumpToState(asyncEvents, targetIndex);
328
+ } catch {
329
+ // Silent fail
330
+ }
331
+ }, [selectedEvent, navigationItems, isPro]);
332
+ const handleCopy = useCallback(async () => {
333
+ if (!selectedEvent) return;
334
+ const eventData = {
335
+ action: selectedEvent.action,
336
+ timestamp: selectedEvent.timestamp.toISOString(),
337
+ data: selectedEvent.data
338
+ };
339
+ await copyToClipboard(eventData);
340
+ }, [selectedEvent]);
341
+
342
+ // Check if current event supports actions
343
+ const isAsyncStorageEvent = selectedEvent?.storageType !== "mmkv";
344
+ const canUndoEvent = isAsyncStorageEvent && selectedEvent && canUndo(selectedEvent);
396
345
  return /*#__PURE__*/_jsxs(_Fragment, {
397
- children: [/*#__PURE__*/_jsxs(View, {
398
- style: [styles.contentOnly, {
399
- flex: 1,
400
- paddingBottom: !disableInternalFooter && totalEvents > 1 ? 80 : 0
401
- }],
402
- children: [/*#__PURE__*/_jsxs(View, {
403
- style: styles.viewToggleContainer,
404
- children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
405
- style: [styles.viewToggleCard, internalActiveView === "current" && styles.viewToggleCardActive],
406
- onPress: () => handleViewChange("current"),
407
- activeOpacity: 0.8,
408
- children: [/*#__PURE__*/_jsxs(View, {
409
- style: styles.viewToggleContent,
410
- children: [/*#__PURE__*/_jsx(Database, {
411
- size: 16,
412
- color: internalActiveView === "current" ? macOSColors.semantic.info : macOSColors.text.secondary
413
- }), /*#__PURE__*/_jsx(Text, {
414
- style: [styles.viewToggleLabel, internalActiveView === "current" && styles.viewToggleLabelActive],
415
- children: "CURRENT VALUE"
416
- })]
417
- }), /*#__PURE__*/_jsx(Text, {
418
- style: [styles.viewToggleDescription, internalActiveView === "current" && {
419
- color: macOSColors.text.primary
420
- }],
421
- children: "View the current stored value"
422
- })]
423
- }), /*#__PURE__*/_jsxs(TouchableOpacity, {
424
- style: [styles.viewToggleCard, internalActiveView === "diff" && styles.viewToggleCardActive],
425
- onPress: () => handleViewChange("diff"),
426
- activeOpacity: 0.8,
427
- children: [/*#__PURE__*/_jsxs(View, {
428
- style: styles.viewToggleContent,
429
- children: [/*#__PURE__*/_jsx(GitBranch, {
430
- size: 16,
431
- color: internalActiveView === "diff" ? macOSColors.semantic.success : macOSColors.text.secondary
432
- }), /*#__PURE__*/_jsx(Text, {
433
- style: [styles.viewToggleLabel, internalActiveView === "diff" && styles.viewToggleLabelActive],
434
- children: "DIFF VIEW"
435
- })]
436
- }), /*#__PURE__*/_jsx(Text, {
437
- style: [styles.viewToggleDescription, internalActiveView === "diff" && {
438
- color: macOSColors.text.primary
439
- }],
440
- children: "Compare changes between versions"
441
- })]
442
- })]
443
- }), lockedEventCount > 0 && /*#__PURE__*/_jsxs(View, {
444
- style: styles.lockedEventsBannerGlobal,
445
- children: [/*#__PURE__*/_jsx(Lock, {
446
- size: 14,
447
- color: macOSColors.semantic.warning
448
- }), /*#__PURE__*/_jsxs(Text, {
449
- style: styles.lockedEventsBannerText,
450
- children: [lockedEventCount, " older ", lockedEventCount === 1 ? 'event' : 'events', " locked"]
451
- }), /*#__PURE__*/_jsx(Text, {
452
- style: styles.lockedEventsBannerSubtext,
453
- children: "Upgrade to Pro for full history"
454
- })]
455
- }), internalActiveView === "current" && renderCurrentValue(), internalActiveView === "diff" && renderDiff()]
456
- }), (isLeftPickerOpen || isRightPickerOpen) && /*#__PURE__*/_jsxs(View, {
457
- style: styles.pickerOverlay,
458
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
459
- style: styles.pickerBackdrop,
460
- activeOpacity: 1,
461
- onPress: () => {
462
- setIsLeftPickerOpen(false);
463
- setIsRightPickerOpen(false);
464
- }
465
- }), /*#__PURE__*/_jsxs(View, {
466
- style: [styles.pickerCard, isLeftPickerOpen && styles.pickerCardLeft, isRightPickerOpen && styles.pickerCardRight],
467
- children: [/*#__PURE__*/_jsxs(View, {
468
- style: styles.pickerHeader,
469
- children: [/*#__PURE__*/_jsxs(Text, {
470
- style: [styles.pickerTitle, isLeftPickerOpen && styles.pickerTitleLeft, isRightPickerOpen && styles.pickerTitleRight],
471
- children: ["Select ", isLeftPickerOpen ? "PREV" : "CUR", " Event"]
472
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
473
- onPress: () => {
474
- setIsLeftPickerOpen(false);
475
- setIsRightPickerOpen(false);
476
- },
477
- style: styles.pickerClose,
478
- accessibilityLabel: "Close event picker",
479
- children: /*#__PURE__*/_jsx(X, {
480
- size: 16,
481
- color: macOSColors.text.secondary
482
- })
483
- })]
484
- }), /*#__PURE__*/_jsx(View, {
485
- style: styles.pickerDivider
486
- }), /*#__PURE__*/_jsx(ScrollView, {
487
- style: styles.pickerScroll,
488
- contentContainerStyle: styles.pickerList,
489
- showsVerticalScrollIndicator: true,
490
- nestedScrollEnabled: true,
491
- children: navigationItems.slice(0, maxAccessibleEventIndex + 1).map((item, idx) => {
492
- const disabled = isLeftPickerOpen ? idx >= rightIndex : idx <= leftIndex;
493
- const isSelected = isLeftPickerOpen ? idx === leftIndex : idx === rightIndex;
494
- return /*#__PURE__*/_jsxs(TouchableOpacity, {
495
- disabled: disabled,
496
- onPress: () => {
497
- if (isLeftPickerOpen) {
498
- setLeftIndex(Math.min(idx, rightIndex - 1));
499
- setIsLeftPickerOpen(false);
500
- } else {
501
- setRightIndex(Math.max(idx, leftIndex + 1));
502
- setIsRightPickerOpen(false);
503
- }
504
- },
505
- style: [styles.pickerItem, isSelected && styles.pickerItemSelected, isSelected && isLeftPickerOpen && styles.pickerItemSelectedLeft, isSelected && isRightPickerOpen && styles.pickerItemSelectedRight, disabled && styles.pickerItemDisabled],
506
- children: [/*#__PURE__*/_jsxs(Text, {
507
- style: styles.pickerIndex,
508
- children: ["#", idx + 1]
509
- }), /*#__PURE__*/_jsx(Text, {
510
- style: styles.pickerTime,
511
- children: formatTimeWithMs(item.timestamp)
512
- }), /*#__PURE__*/_jsxs(Text, {
513
- style: styles.pickerRelative,
514
- children: ["(", formatRelativeTime(item.timestamp), ")"]
515
- }), (() => {
516
- const targetOld = isLeftPickerOpen ? item : navigationItems[leftIndex];
517
- const targetNew = isLeftPickerOpen ? navigationItems[rightIndex] : item;
518
- const oldVal = parseValue(targetOld.data?.value);
519
- const newVal = parseValue(targetNew.data?.value);
520
- const diffs = computeLineDiff(oldVal, newVal, {
521
- compareMethod: "words",
522
- disableWordDiff: false,
523
- showDiffOnly: false,
524
- contextLines: 0
525
- });
526
- const added = diffs.filter(d => d.type === DiffType.ADDED).length;
527
- const removed = diffs.filter(d => d.type === DiffType.REMOVED).length;
528
- const modified = diffs.filter(d => d.type === DiffType.MODIFIED).length;
529
- return /*#__PURE__*/_jsxs(View, {
530
- style: styles.pickerCounts,
531
- children: [/*#__PURE__*/_jsxs(Text, {
532
- style: [styles.pickerCountText, {
533
- color: diffThemes.devToolsDefault.summaryAddedText
534
- }],
535
- children: ["+", added]
536
- }), /*#__PURE__*/_jsxs(Text, {
537
- style: [styles.pickerCountText, {
538
- color: diffThemes.devToolsDefault.summaryRemovedText
539
- }],
540
- children: ["-", removed]
541
- }), /*#__PURE__*/_jsxs(Text, {
542
- style: [styles.pickerCountText, {
543
- color: diffThemes.devToolsDefault.summaryModifiedText
544
- }],
545
- children: ["~", modified]
546
- })]
547
- });
548
- })()]
549
- }, idx);
550
- })
551
- })]
346
+ children: [isAsyncStorageEvent && /*#__PURE__*/_jsxs(View, {
347
+ style: styles.actionButtonsContainer,
348
+ children: [/*#__PURE__*/_jsx(StorageEventActionButton, {
349
+ type: "undo",
350
+ text: "UNDO",
351
+ onPress: handleUndo,
352
+ disabled: !canUndoEvent,
353
+ locked: !isPro
354
+ }), /*#__PURE__*/_jsx(StorageEventActionButton, {
355
+ type: "jump",
356
+ text: "JUMP",
357
+ onPress: handleJump,
358
+ locked: !isPro
359
+ }), /*#__PURE__*/_jsx(StorageEventActionButton, {
360
+ type: "copy",
361
+ text: "COPY",
362
+ onPress: handleCopy
552
363
  })]
553
- }), !disableInternalFooter && /*#__PURE__*/_jsx(EventStepperFooter, {
554
- currentIndex: selectedEventIndex,
555
- totalItems: totalEvents,
556
- onPrevious: () => onEventIndexChange(Math.max(0, selectedEventIndex - 1)),
557
- onNext: () => onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1)),
558
- itemLabel: "Event",
559
- subtitle: formatRelativeTime(navigationItems[selectedEventIndex]?.timestamp),
560
- absolute: true
364
+ }), /*#__PURE__*/_jsx(EventHistoryViewer
365
+ // View Toggle
366
+ , {
367
+ activeView: activeView,
368
+ onViewChange: handleViewChange,
369
+ currentViewLabel: "CURRENT VALUE",
370
+ currentViewDescription: "View the current stored value",
371
+ currentViewIcon: Database,
372
+ diffViewLabel: "DIFF VIEW",
373
+ diffViewDescription: "Compare changes between versions",
374
+ diffViewIcon: GitBranch
375
+ // Current View
376
+ ,
377
+ renderCurrentView: renderCurrentView
378
+ // Diff View
379
+ ,
380
+ diffModeTabs: diffModeTabs,
381
+ activeDiffMode: diffMode,
382
+ onDiffModeChange: handleDiffModeChange,
383
+ renderDiffContent: renderDiffContent
384
+ // Compare Bar
385
+ ,
386
+ leftEvent: leftEventInfo,
387
+ rightEvent: rightEventInfo,
388
+ showCompareNavigation: true,
389
+ onLeftPrevious: handleLeftPrevious,
390
+ onLeftNext: handleLeftNext,
391
+ onRightPrevious: handleRightPrevious,
392
+ onRightNext: handleRightNext,
393
+ canLeftPrevious: leftIndex > 0,
394
+ canLeftNext: leftIndex < rightIndex - 1,
395
+ canRightPrevious: rightIndex > leftIndex + 1,
396
+ canRightNext: rightIndex < totalEvents - 1
397
+ // Footer
398
+ ,
399
+ disableInternalFooter: disableInternalFooter,
400
+ footerCurrentIndex: selectedEventIndex,
401
+ footerTotalItems: totalEvents,
402
+ footerItemLabel: "Event",
403
+ footerSubtitle: formatRelativeTime(selectedEvent?.timestamp),
404
+ onFooterPrevious: handleFooterPrevious,
405
+ onFooterNext: handleFooterNext
406
+ }), /*#__PURE__*/_jsx(ProUpgradeModal, {
407
+ visible: showProModal,
408
+ onClose: () => setShowProModal(false),
409
+ featureName: "Storage Time-Travel"
561
410
  })]
562
411
  });
563
412
  }
564
413
 
565
- // External footer component to be rendered by the modal outside the ScrollView
414
+ /**
415
+ * External footer component for modal use
416
+ */
566
417
  export function StorageEventDetailFooter({
567
418
  conversation,
568
419
  selectedEventIndex = 0,
569
420
  onEventIndexChange = () => {}
570
421
  }) {
571
- // Check Pro status internally
572
- const useIsPro = getUseIsPro();
573
- const isPro = useIsPro();
574
422
  const navigationItems = conversation.events.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
575
423
  const totalEvents = navigationItems.length;
576
-
577
- // Free tier limit for navigation
578
- const maxAccessibleIndex = isPro ? totalEvents - 1 : Math.min(FREE_TIER_EVENT_HISTORY_LIMIT - 1, totalEvents - 1);
579
- const lockedCount = isPro ? 0 : Math.max(0, totalEvents - FREE_TIER_EVENT_HISTORY_LIMIT);
580
-
581
- // Calculate display total (accessible events for free users)
582
- const displayTotal = isPro ? totalEvents : Math.min(totalEvents, FREE_TIER_EVENT_HISTORY_LIMIT);
583
424
  return /*#__PURE__*/_jsx(EventStepperFooter, {
584
425
  currentIndex: selectedEventIndex,
585
- totalItems: displayTotal,
426
+ totalItems: totalEvents,
586
427
  onPrevious: () => onEventIndexChange(Math.max(0, selectedEventIndex - 1)),
587
- onNext: () => onEventIndexChange(Math.min(maxAccessibleIndex, selectedEventIndex + 1)),
428
+ onNext: () => onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1)),
588
429
  itemLabel: "Event",
589
- subtitle: lockedCount > 0 ? `${formatRelativeTime(navigationItems[selectedEventIndex]?.timestamp)} • ${lockedCount} locked` : formatRelativeTime(navigationItems[selectedEventIndex]?.timestamp)
430
+ subtitle: formatRelativeTime(navigationItems[selectedEventIndex]?.timestamp)
590
431
  });
591
432
  }
592
433
  const styles = StyleSheet.create({
593
- contentOnly: {
594
- flex: 1,
595
- backgroundColor: macOSColors.background.base
596
- },
597
- fullPageSection: {
598
- flex: 1,
599
- paddingHorizontal: 14,
600
- paddingVertical: 10
601
- },
602
- emptyState: {
603
- flex: 1,
604
- alignItems: "center",
434
+ // Action Buttons
435
+ actionButtonsContainer: {
436
+ flexDirection: "row",
605
437
  justifyContent: "center",
606
- paddingVertical: 48
607
- },
608
- emptyText: {
609
- marginTop: 12,
610
- fontSize: 14,
611
- color: macOSColors.text.secondary,
612
- fontFamily: "monospace"
438
+ alignItems: "center",
439
+ gap: 8,
440
+ paddingHorizontal: 12,
441
+ paddingVertical: 10,
442
+ backgroundColor: buoyColors.card,
443
+ borderBottomWidth: 1,
444
+ borderBottomColor: buoyColors.border
613
445
  },
614
- card: {
446
+ // Content Card
447
+ contentCard: {
615
448
  backgroundColor: macOSColors.background.card,
616
449
  borderRadius: 14,
617
450
  padding: 14,
618
451
  borderWidth: 1,
619
- borderColor: macOSColors.border.default
620
- },
621
- valueContent: {
622
- marginTop: 4
452
+ borderColor: macOSColors.border.default,
453
+ shadowColor: macOSColors.semantic.info,
454
+ shadowOffset: {
455
+ width: 0,
456
+ height: 2
457
+ },
458
+ shadowOpacity: 0.04,
459
+ shadowRadius: 16,
460
+ elevation: 2
623
461
  },
624
462
  valueHeader: {
625
463
  flexDirection: "row",
626
464
  alignItems: "center",
627
465
  justifyContent: "space-between",
628
- marginBottom: 4
466
+ marginBottom: 8
629
467
  },
630
468
  valueLabel: {
631
469
  fontSize: 10,
@@ -643,9 +481,7 @@ const styles = StyleSheet.create({
643
481
  actionBadge: {
644
482
  paddingHorizontal: 8,
645
483
  paddingVertical: 2,
646
- borderRadius: 4,
647
- borderWidth: 1,
648
- borderColor: "transparent"
484
+ borderRadius: 4
649
485
  },
650
486
  actionText: {
651
487
  fontSize: 9,
@@ -666,7 +502,7 @@ const styles = StyleSheet.create({
666
502
  fontFamily: "monospace"
667
503
  },
668
504
  valueBox: {
669
- backgroundColor: macOSColors.background.card,
505
+ backgroundColor: macOSColors.background.base,
670
506
  borderRadius: 6,
671
507
  borderWidth: 1,
672
508
  borderColor: macOSColors.border.input,
@@ -678,343 +514,15 @@ const styles = StyleSheet.create({
678
514
  fontFamily: "monospace",
679
515
  lineHeight: 18
680
516
  },
681
- // Compare picker styles
682
- compareBar: {
683
- flexDirection: "row",
684
- alignItems: "center",
685
- justifyContent: "space-between",
686
- backgroundColor: macOSColors.background.card,
687
- borderWidth: 1,
688
- borderColor: macOSColors.border.default,
689
- borderRadius: 6,
690
- paddingHorizontal: 8,
691
- paddingVertical: 6,
692
- marginBottom: 8,
693
- gap: 8
694
- },
695
- compareSide: {
696
- flex: 1
697
- },
698
- compareLabelRow: {
699
- flexDirection: "row",
700
- alignItems: "center",
701
- gap: 6,
702
- marginBottom: 2
703
- },
704
- compareLabel: {
705
- fontSize: 10,
706
- fontFamily: "monospace",
707
- fontWeight: "700",
708
- letterSpacing: 0.5,
709
- textTransform: "uppercase"
710
- },
711
- compareActionBadge: {
517
+ // Action badges for compare bar
518
+ actionBadgeSmall: {
712
519
  paddingHorizontal: 6,
713
520
  paddingVertical: 1,
714
521
  borderRadius: 3
715
522
  },
716
- compareActionText: {
523
+ actionTextSmall: {
717
524
  fontSize: 8,
718
525
  fontWeight: "700",
719
- fontFamily: "monospace",
720
- letterSpacing: 0.3
721
- },
722
- compareControls: {
723
- flexDirection: "row",
724
- alignItems: "center",
725
- gap: 6
726
- },
727
- compareBtn: {
728
- width: 26,
729
- height: 26,
730
- borderRadius: 6,
731
- backgroundColor: macOSColors.background.card,
732
- borderWidth: 1,
733
- borderColor: macOSColors.border.default,
734
- alignItems: "center",
735
- justifyContent: "center"
736
- },
737
- compareBtnDisabled: {
738
- opacity: 0.4
739
- },
740
- compareMeta: {
741
- flex: 1
742
- },
743
- compareTime: {
744
- fontSize: 11,
745
- color: macOSColors.text.primary,
746
- fontFamily: "monospace"
747
- },
748
- compareIndex: {
749
- fontSize: 10,
750
- color: macOSColors.text.secondary,
751
- fontFamily: "monospace"
752
- },
753
- compareRelative: {
754
- fontSize: 10,
755
- color: macOSColors.text.secondary,
756
- fontFamily: "monospace"
757
- },
758
- compareDivider: {
759
- width: 1,
760
- height: 34,
761
- backgroundColor: macOSColors.background.input
762
- },
763
- pickerOverlay: {
764
- ...StyleSheet.absoluteFillObject,
765
- zIndex: 20,
766
- justifyContent: "center",
767
- alignItems: "center"
768
- },
769
- pickerBackdrop: {
770
- ...StyleSheet.absoluteFillObject,
771
- backgroundColor: "rgba(0,0,0,0.65)"
772
- },
773
- pickerCard: {
774
- width: "86%",
775
- maxHeight: 320,
776
- backgroundColor: macOSColors.background.card,
777
- borderRadius: 16,
778
- borderWidth: 2,
779
- padding: 16,
780
- shadowOffset: {
781
- width: 0,
782
- height: 0
783
- },
784
- shadowOpacity: 0.5,
785
- shadowRadius: 40,
786
- elevation: 15
787
- },
788
- pickerCardLeft: {
789
- borderColor: macOSColors.semantic.debug,
790
- shadowColor: macOSColors.semantic.debug
791
- },
792
- pickerCardRight: {
793
- borderColor: macOSColors.semantic.success,
794
- shadowColor: macOSColors.semantic.success
795
- },
796
- pickerHeader: {
797
- flexDirection: "row",
798
- alignItems: "center",
799
- justifyContent: "space-between"
800
- },
801
- pickerClose: {
802
- padding: 6,
803
- borderRadius: 6,
804
- backgroundColor: macOSColors.background.card,
805
- borderWidth: 1,
806
- borderColor: macOSColors.border.default
807
- },
808
- pickerDivider: {
809
- height: 1,
810
- backgroundColor: macOSColors.background.input,
811
- marginVertical: 8
812
- },
813
- pickerScroll: {
814
- maxHeight: 260
815
- },
816
- pickerTitle: {
817
- fontSize: 13,
818
- fontWeight: "700",
819
- fontFamily: "monospace",
820
- textTransform: "uppercase",
821
- marginBottom: 8,
822
- letterSpacing: 0.6
823
- },
824
- pickerTitleLeft: {
825
- color: macOSColors.semantic.debug
826
- },
827
- pickerTitleRight: {
828
- color: macOSColors.semantic.success
829
- },
830
- pickerList: {
831
- gap: 4
832
- },
833
- pickerItem: {
834
- paddingVertical: 10,
835
- paddingHorizontal: 12,
836
- borderRadius: 10,
837
- backgroundColor: macOSColors.background.base,
838
- borderWidth: 1,
839
- borderColor: macOSColors.border.default,
840
- flexDirection: "row",
841
- alignItems: "center",
842
- gap: 8,
843
- marginBottom: 6
844
- },
845
- pickerItemSelected: {
846
- borderWidth: 1.5,
847
- shadowOffset: {
848
- width: 0,
849
- height: 2
850
- },
851
- shadowOpacity: 0.25,
852
- shadowRadius: 12,
853
- elevation: 4
854
- },
855
- pickerItemSelectedLeft: {
856
- backgroundColor: macOSColors.semantic.debug + "1A",
857
- borderColor: macOSColors.semantic.debug,
858
- shadowColor: macOSColors.semantic.debug
859
- },
860
- pickerItemSelectedRight: {
861
- backgroundColor: macOSColors.semantic.successBackground + "30",
862
- borderColor: macOSColors.semantic.success,
863
- shadowColor: macOSColors.semantic.success
864
- },
865
- pickerItemDisabled: {
866
- opacity: 0.4
867
- },
868
- pickerIndex: {
869
- fontSize: 10,
870
- color: macOSColors.text.secondary,
871
- fontFamily: "monospace",
872
- width: 40
873
- },
874
- pickerTime: {
875
- fontSize: 11,
876
- color: macOSColors.text.primary,
877
- fontFamily: "monospace",
878
- flex: 1
879
- },
880
- pickerRelative: {
881
- fontSize: 10,
882
- color: macOSColors.text.secondary,
883
526
  fontFamily: "monospace"
884
- },
885
- pickerCounts: {
886
- flexDirection: "row",
887
- alignItems: "center",
888
- gap: 8,
889
- marginLeft: "auto"
890
- },
891
- pickerCountText: {
892
- fontSize: 10,
893
- fontFamily: "monospace",
894
- fontWeight: "700"
895
- },
896
- diffViewerTabs: {
897
- flexDirection: "row",
898
- alignItems: "center",
899
- justifyContent: "space-around",
900
- backgroundColor: macOSColors.background.card,
901
- borderWidth: 1,
902
- borderColor: macOSColors.border.default,
903
- borderRadius: 6,
904
- paddingHorizontal: 4,
905
- paddingVertical: 4,
906
- marginBottom: 8,
907
- gap: 4
908
- },
909
- diffViewerTab: {
910
- flex: 1,
911
- paddingVertical: 8,
912
- paddingHorizontal: 12,
913
- borderRadius: 4,
914
- alignItems: "center",
915
- backgroundColor: "transparent"
916
- },
917
- diffViewerTabActive: {
918
- backgroundColor: macOSColors.semantic.infoBackground,
919
- borderWidth: 1,
920
- borderColor: macOSColors.semantic.info + "40"
921
- },
922
- diffViewerTabText: {
923
- fontSize: 11,
924
- fontFamily: "monospace",
925
- fontWeight: "600",
926
- color: macOSColors.text.secondary,
927
- letterSpacing: 0.5
928
- },
929
- diffViewerTabTextActive: {
930
- color: macOSColors.text.primary
931
- },
932
- contentCard: {
933
- backgroundColor: macOSColors.background.card,
934
- borderRadius: 14,
935
- padding: 14,
936
- borderWidth: 1,
937
- borderColor: macOSColors.border.default,
938
- shadowColor: macOSColors.semantic.info,
939
- shadowOffset: {
940
- width: 0,
941
- height: 2
942
- },
943
- shadowOpacity: 0.04,
944
- shadowRadius: 16,
945
- elevation: 2
946
- },
947
- // View Toggle Cards
948
- viewToggleContainer: {
949
- flexDirection: "row",
950
- gap: 12,
951
- padding: 14,
952
- backgroundColor: macOSColors.background.base
953
- },
954
- viewToggleCard: {
955
- flex: 1,
956
- backgroundColor: macOSColors.background.card,
957
- borderRadius: 14,
958
- borderWidth: 1,
959
- borderColor: macOSColors.border.default,
960
- padding: 14,
961
- gap: 8
962
- },
963
- viewToggleCardActive: {
964
- borderWidth: 1.5,
965
- borderColor: macOSColors.semantic.info,
966
- backgroundColor: macOSColors.semantic.infoBackground + "30",
967
- shadowColor: macOSColors.semantic.info,
968
- shadowOffset: {
969
- width: 0,
970
- height: 2
971
- },
972
- shadowOpacity: 0.1,
973
- shadowRadius: 8,
974
- elevation: 3
975
- },
976
- viewToggleContent: {
977
- flexDirection: "row",
978
- alignItems: "center",
979
- gap: 8
980
- },
981
- viewToggleLabel: {
982
- fontSize: 12,
983
- fontWeight: "700",
984
- letterSpacing: 0.5,
985
- color: macOSColors.text.secondary,
986
- textTransform: "uppercase"
987
- },
988
- viewToggleLabelActive: {
989
- color: macOSColors.text.primary
990
- },
991
- viewToggleDescription: {
992
- fontSize: 11,
993
- color: macOSColors.text.muted,
994
- lineHeight: 16
995
- },
996
- lockedEventsBannerGlobal: {
997
- flexDirection: "row",
998
- alignItems: "center",
999
- gap: 8,
1000
- backgroundColor: macOSColors.semantic.warning + "15",
1001
- borderWidth: 1,
1002
- borderColor: macOSColors.semantic.warning + "30",
1003
- borderRadius: 10,
1004
- paddingVertical: 12,
1005
- paddingHorizontal: 14,
1006
- marginHorizontal: 14,
1007
- marginBottom: 4
1008
- },
1009
- lockedEventsBannerText: {
1010
- fontSize: 12,
1011
- fontWeight: "600",
1012
- color: macOSColors.semantic.warning,
1013
- fontFamily: "monospace"
1014
- },
1015
- lockedEventsBannerSubtext: {
1016
- fontSize: 11,
1017
- color: macOSColors.text.secondary,
1018
- marginLeft: "auto"
1019
527
  }
1020
528
  });