@buoy-gg/shared-ui 1.7.7 → 2.1.1

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 (137) hide show
  1. package/lib/commonjs/JsModal.js +33 -8
  2. package/lib/commonjs/clipboard/clipboard-impl.js +1 -1
  3. package/lib/commonjs/clipboard/copyToClipboard.js +0 -2
  4. package/lib/commonjs/dataViewer/diffThemes.js +35 -32
  5. package/lib/commonjs/history/HistoryProvider.js +246 -0
  6. package/lib/commonjs/history/components/HistoryEntryRow.js +146 -0
  7. package/lib/commonjs/history/components/HistoryList.js +174 -0
  8. package/lib/commonjs/history/components/index.js +25 -0
  9. package/lib/commonjs/history/index.js +61 -0
  10. package/lib/commonjs/history/types.js +5 -0
  11. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  12. package/lib/commonjs/hooks/useSafeAreaInsets.js +1 -22
  13. package/lib/commonjs/index.js +37 -46
  14. package/lib/commonjs/license/FeatureGate.js +13 -42
  15. package/lib/commonjs/license/index.js +43 -1
  16. package/lib/commonjs/settings/components/BubbleSettingsSection.js +7 -7
  17. package/lib/commonjs/storage/devToolsStorageKeys.js +11 -0
  18. package/lib/commonjs/ui/components/CopyButton.js +14 -29
  19. package/lib/commonjs/ui/components/DevToolsCard.js +106 -0
  20. package/lib/commonjs/ui/components/EventHistoryViewer/EventHistoryViewer.js +2 -1
  21. package/lib/commonjs/ui/components/ExpandablePopover.js +13 -13
  22. package/lib/commonjs/ui/components/index.js +13 -0
  23. package/lib/commonjs/utils/formatting/httpFormatting.js +0 -6
  24. package/lib/commonjs/utils/index.js +0 -31
  25. package/lib/module/JsModal.js +33 -8
  26. package/lib/module/clipboard/clipboard-impl.js +1 -1
  27. package/lib/module/clipboard/copyToClipboard.js +0 -2
  28. package/lib/module/dataViewer/diffThemes.js +35 -32
  29. package/lib/module/history/HistoryProvider.js +237 -0
  30. package/lib/module/history/components/HistoryEntryRow.js +142 -0
  31. package/lib/module/history/components/HistoryList.js +169 -0
  32. package/lib/module/history/components/index.js +8 -0
  33. package/lib/module/history/index.js +15 -0
  34. package/lib/module/history/types.js +3 -0
  35. package/lib/module/hooks/safe-area-impl.js +1 -1
  36. package/lib/module/hooks/useSafeAreaInsets.js +0 -20
  37. package/lib/module/index.js +5 -4
  38. package/lib/module/license/FeatureGate.js +11 -40
  39. package/lib/module/license/index.js +41 -1
  40. package/lib/module/settings/components/BubbleSettingsSection.js +7 -7
  41. package/lib/module/storage/devToolsStorageKeys.js +11 -0
  42. package/lib/module/ui/components/CopyButton.js +12 -28
  43. package/lib/module/ui/components/DevToolsCard.js +102 -0
  44. package/lib/module/ui/components/EventHistoryViewer/EventHistoryViewer.js +2 -1
  45. package/lib/module/ui/components/ExpandablePopover.js +14 -14
  46. package/lib/module/ui/components/index.js +1 -0
  47. package/lib/module/utils/formatting/httpFormatting.js +0 -6
  48. package/lib/module/utils/index.js +0 -1
  49. package/lib/typescript/commonjs/JsModal.d.ts +2 -0
  50. package/lib/typescript/commonjs/JsModal.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/clipboard/clipboard-impl.d.ts +1 -1
  52. package/lib/typescript/commonjs/clipboard/copyToClipboard.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/dataViewer/diffThemes.d.ts +1 -1
  54. package/lib/typescript/commonjs/dataViewer/diffThemes.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/history/HistoryProvider.d.ts +56 -0
  56. package/lib/typescript/commonjs/history/HistoryProvider.d.ts.map +1 -0
  57. package/lib/typescript/commonjs/history/components/HistoryEntryRow.d.ts +22 -0
  58. package/lib/typescript/commonjs/history/components/HistoryEntryRow.d.ts.map +1 -0
  59. package/lib/typescript/commonjs/history/components/HistoryList.d.ts +47 -0
  60. package/lib/typescript/commonjs/history/components/HistoryList.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/history/components/index.d.ts +6 -0
  62. package/lib/typescript/commonjs/history/components/index.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/history/index.d.ts +9 -0
  64. package/lib/typescript/commonjs/history/index.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/history/types.d.ts +171 -0
  66. package/lib/typescript/commonjs/history/types.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  68. package/lib/typescript/commonjs/hooks/useSafeAreaInsets.d.ts +0 -13
  69. package/lib/typescript/commonjs/hooks/useSafeAreaInsets.d.ts.map +1 -1
  70. package/lib/typescript/commonjs/index.d.ts +3 -2
  71. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/license/FeatureGate.d.ts.map +1 -1
  73. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts +5 -0
  74. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
  75. package/lib/typescript/commonjs/license/index.d.ts +15 -0
  76. package/lib/typescript/commonjs/license/index.d.ts.map +1 -1
  77. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts +11 -0
  78. package/lib/typescript/commonjs/storage/devToolsStorageKeys.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/ui/components/CopyButton.d.ts +0 -2
  80. package/lib/typescript/commonjs/ui/components/CopyButton.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts +54 -0
  82. package/lib/typescript/commonjs/ui/components/DevToolsCard.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/EventHistoryViewer.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/ui/components/index.d.ts +2 -0
  85. package/lib/typescript/commonjs/ui/components/index.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/utils/formatting/httpFormatting.d.ts.map +1 -1
  87. package/lib/typescript/commonjs/utils/index.d.ts +0 -1
  88. package/lib/typescript/commonjs/utils/index.d.ts.map +1 -1
  89. package/lib/typescript/module/JsModal.d.ts +2 -0
  90. package/lib/typescript/module/JsModal.d.ts.map +1 -1
  91. package/lib/typescript/module/clipboard/clipboard-impl.d.ts +1 -1
  92. package/lib/typescript/module/clipboard/copyToClipboard.d.ts.map +1 -1
  93. package/lib/typescript/module/dataViewer/diffThemes.d.ts +1 -1
  94. package/lib/typescript/module/dataViewer/diffThemes.d.ts.map +1 -1
  95. package/lib/typescript/module/history/HistoryProvider.d.ts +56 -0
  96. package/lib/typescript/module/history/HistoryProvider.d.ts.map +1 -0
  97. package/lib/typescript/module/history/components/HistoryEntryRow.d.ts +22 -0
  98. package/lib/typescript/module/history/components/HistoryEntryRow.d.ts.map +1 -0
  99. package/lib/typescript/module/history/components/HistoryList.d.ts +47 -0
  100. package/lib/typescript/module/history/components/HistoryList.d.ts.map +1 -0
  101. package/lib/typescript/module/history/components/index.d.ts +6 -0
  102. package/lib/typescript/module/history/components/index.d.ts.map +1 -0
  103. package/lib/typescript/module/history/index.d.ts +9 -0
  104. package/lib/typescript/module/history/index.d.ts.map +1 -0
  105. package/lib/typescript/module/history/types.d.ts +171 -0
  106. package/lib/typescript/module/history/types.d.ts.map +1 -0
  107. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  108. package/lib/typescript/module/hooks/useSafeAreaInsets.d.ts +0 -13
  109. package/lib/typescript/module/hooks/useSafeAreaInsets.d.ts.map +1 -1
  110. package/lib/typescript/module/index.d.ts +3 -2
  111. package/lib/typescript/module/index.d.ts.map +1 -1
  112. package/lib/typescript/module/license/FeatureGate.d.ts.map +1 -1
  113. package/lib/typescript/module/license/LicenseEntryModal.d.ts +5 -0
  114. package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
  115. package/lib/typescript/module/license/index.d.ts +15 -0
  116. package/lib/typescript/module/license/index.d.ts.map +1 -1
  117. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts +11 -0
  118. package/lib/typescript/module/storage/devToolsStorageKeys.d.ts.map +1 -1
  119. package/lib/typescript/module/ui/components/CopyButton.d.ts +0 -2
  120. package/lib/typescript/module/ui/components/CopyButton.d.ts.map +1 -1
  121. package/lib/typescript/module/ui/components/DevToolsCard.d.ts +54 -0
  122. package/lib/typescript/module/ui/components/DevToolsCard.d.ts.map +1 -0
  123. package/lib/typescript/module/ui/components/EventHistoryViewer/EventHistoryViewer.d.ts.map +1 -1
  124. package/lib/typescript/module/ui/components/index.d.ts +2 -0
  125. package/lib/typescript/module/ui/components/index.d.ts.map +1 -1
  126. package/lib/typescript/module/utils/formatting/httpFormatting.d.ts.map +1 -1
  127. package/lib/typescript/module/utils/index.d.ts +0 -1
  128. package/lib/typescript/module/utils/index.d.ts.map +1 -1
  129. package/package.json +31 -7
  130. package/scripts/detect-clipboard.js +63 -1
  131. package/scripts/detect-safe-area.js +63 -1
  132. package/lib/commonjs/utils/safeAsyncStorage.js +0 -71
  133. package/lib/module/utils/safeAsyncStorage.js +0 -64
  134. package/lib/typescript/commonjs/utils/safeAsyncStorage.d.ts +0 -35
  135. package/lib/typescript/commonjs/utils/safeAsyncStorage.d.ts.map +0 -1
  136. package/lib/typescript/module/utils/safeAsyncStorage.d.ts +0 -35
  137. package/lib/typescript/module/utils/safeAsyncStorage.d.ts.map +0 -1
@@ -48,51 +48,54 @@ export const gitClassicTheme = {
48
48
 
49
49
  /**
50
50
  * Dev Tools Default Theme
51
- * Clean dark theme using our gameUIColors
51
+ * Clean dark theme matching Buoy website brand colors
52
52
  */
53
53
  export const devToolsDefaultTheme = {
54
54
  name: "Dev Tools Default",
55
- description: "Clean dark theme with our game UI colors",
56
- // Use our gameUIColors-inspired dark theme
57
- background: "#0A0E1A",
58
- panelBackground: "#0F1420",
59
- headerBackground: "#141925",
60
- // Diff colors with our cyan/yellow/red scheme
61
- addedBackground: "rgba(74, 255, 159, 0.1)",
62
- removedBackground: "rgba(255, 82, 82, 0.1)",
63
- modifiedBackground: "rgba(0, 184, 230, 0.1)",
55
+ description: "Clean dark theme with Buoy brand colors",
56
+ // Surface colors (matching website dark theme)
57
+ background: "#121212",
58
+ panelBackground: "#1A1A1A",
59
+ headerBackground: "#1A1A1A",
60
+ // Diff colors using website's semantic colors
61
+ // Added: Primary teal (#20C997)
62
+ // Removed: Error red (#EF4444)
63
+ // Modified: Secondary purple (#9B70E0)
64
+ addedBackground: "rgba(32, 201, 151, 0.12)",
65
+ removedBackground: "rgba(239, 68, 68, 0.12)",
66
+ modifiedBackground: "rgba(155, 112, 224, 0.12)",
64
67
  unchangedBackground: "transparent",
65
68
  contextBackground: "rgba(255, 255, 255, 0.02)",
66
69
  // Text colors
67
- addedText: "#4AFF9F",
68
- removedText: "#FF5252",
69
- modifiedText: "#00B8E6",
70
- unchangedText: "#B8BFC9",
70
+ addedText: "#20C997",
71
+ removedText: "#EF4444",
72
+ modifiedText: "#9B70E0",
73
+ unchangedText: "#E0E0E0",
71
74
  // Word-level highlights
72
- addedWordHighlight: "rgba(74, 255, 159, 0.3)",
73
- removedWordHighlight: "rgba(255, 82, 82, 0.3)",
75
+ addedWordHighlight: "rgba(32, 201, 151, 0.3)",
76
+ removedWordHighlight: "rgba(239, 68, 68, 0.3)",
74
77
  // UI elements
75
- lineNumberBackground: "#0A0E1A",
76
- lineNumberText: "#7A8599",
77
- lineNumberBorder: "#1F2937",
78
+ lineNumberBackground: "#121212",
79
+ lineNumberText: "#A0A0A0",
80
+ lineNumberBorder: "#333333",
78
81
  // Markers
79
- markerAddedBackground: "rgba(74, 255, 159, 0.2)",
80
- markerRemovedBackground: "rgba(255, 82, 82, 0.2)",
81
- markerModifiedBackground: "rgba(0, 184, 230, 0.2)",
82
- markerText: "#7A8599",
82
+ markerAddedBackground: "rgba(32, 201, 151, 0.2)",
83
+ markerRemovedBackground: "rgba(239, 68, 68, 0.2)",
84
+ markerModifiedBackground: "rgba(155, 112, 224, 0.2)",
85
+ markerText: "#A0A0A0",
83
86
  // Borders and dividers
84
- borderColor: "#1F2937",
85
- dividerColor: "#1F2937",
87
+ borderColor: "#333333",
88
+ dividerColor: "#333333",
86
89
  // Summary bar
87
- summaryBackground: "#0F1420",
88
- summaryAddedText: "#4AFF9F",
89
- summaryRemovedText: "#FF5252",
90
- summaryModifiedText: "#00B8E6",
90
+ summaryBackground: "#1A1A1A",
91
+ summaryAddedText: "#20C997",
92
+ summaryRemovedText: "#EF4444",
93
+ summaryModifiedText: "#9B70E0",
91
94
  // Empty state
92
- emptyStateText: "#7A8599",
95
+ emptyStateText: "#888888",
93
96
  // Separator
94
- separatorBackground: "#141925",
95
- separatorText: "#7A8599"
97
+ separatorBackground: "#1A1A1A",
98
+ separatorText: "#A0A0A0"
96
99
  };
97
100
 
98
101
  /**
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * HistoryProvider - Context for Universal History DevTools
5
+ *
6
+ * Manages registered adapters and provides unified access to history
7
+ * from Redux, Zustand, MMKV, React Query, etc.
8
+ */
9
+
10
+ import { createContext, useContext, useState, useCallback, useMemo, useEffect } from "react";
11
+
12
+ // =============================================================================
13
+ // CONTEXT VALUE
14
+ // =============================================================================
15
+ import { jsx as _jsx } from "react/jsx-runtime";
16
+ const HistoryContext = /*#__PURE__*/createContext(null);
17
+
18
+ // =============================================================================
19
+ // PROVIDER
20
+ // =============================================================================
21
+
22
+ export function HistoryProvider({
23
+ children,
24
+ initialAdapters = []
25
+ }) {
26
+ // Adapter registry
27
+ const [adapterMap, setAdapterMap] = useState(() => {
28
+ const map = new Map();
29
+ initialAdapters.forEach(reg => {
30
+ map.set(reg.adapter.id, {
31
+ ...reg,
32
+ enabled: reg.enabled ?? true
33
+ });
34
+ });
35
+ return map;
36
+ });
37
+
38
+ // Selected adapter filter
39
+ const [selectedAdapterId, setSelectedAdapterId] = useState(null);
40
+
41
+ // Refresh trigger
42
+ const [refreshKey, setRefreshKey] = useState(0);
43
+ const refresh = useCallback(() => setRefreshKey(k => k + 1), []);
44
+
45
+ // Subscribe to all adapters
46
+ useEffect(() => {
47
+ const unsubscribes = [];
48
+ adapterMap.forEach(reg => {
49
+ if (reg.enabled) {
50
+ const unsub = reg.adapter.subscribe(refresh);
51
+ unsubscribes.push(unsub);
52
+ }
53
+ });
54
+ return () => {
55
+ unsubscribes.forEach(unsub => unsub());
56
+ };
57
+ }, [adapterMap, refresh]);
58
+
59
+ // Get sorted adapters list
60
+ const adapters = useMemo(() => {
61
+ return Array.from(adapterMap.values()).filter(reg => reg.enabled).sort((a, b) => (a.priority ?? 100) - (b.priority ?? 100)).map(reg => reg.adapter);
62
+ }, [adapterMap, refreshKey]);
63
+
64
+ // Get adapter by id
65
+ const getAdapter = useCallback(id => adapterMap.get(id)?.adapter, [adapterMap]);
66
+
67
+ // Register adapter
68
+ const registerAdapter = useCallback(registration => {
69
+ setAdapterMap(prev => {
70
+ const next = new Map(prev);
71
+ next.set(registration.adapter.id, {
72
+ ...registration,
73
+ enabled: registration.enabled ?? true
74
+ });
75
+ return next;
76
+ });
77
+ }, []);
78
+
79
+ // Unregister adapter
80
+ const unregisterAdapter = useCallback(id => {
81
+ setAdapterMap(prev => {
82
+ const next = new Map(prev);
83
+ next.delete(id);
84
+ return next;
85
+ });
86
+ }, []);
87
+
88
+ // Get all entries (combined or filtered)
89
+ const getAllEntries = useCallback(filter => {
90
+ let entries = [];
91
+
92
+ // Collect from all (or selected) adapters
93
+ const sourceAdapters = selectedAdapterId && filter?.sources === undefined ? adapters.filter(a => a.id === selectedAdapterId) : adapters;
94
+ sourceAdapters.forEach(adapter => {
95
+ // Skip if sources filter excludes this adapter
96
+ if (filter?.sources && !filter.sources.includes(adapter.id)) {
97
+ return;
98
+ }
99
+ entries = entries.concat(adapter.getEntries());
100
+ });
101
+
102
+ // Apply filters
103
+ if (filter) {
104
+ entries = applyFilter(entries, filter);
105
+ }
106
+
107
+ // Sort by timestamp (newest first by default)
108
+ entries.sort((a, b) => b.timestamp - a.timestamp);
109
+ return entries;
110
+ }, [adapters, selectedAdapterId, refreshKey]);
111
+
112
+ // Get entry counts per source
113
+ const getEntryCounts = useCallback(() => {
114
+ const counts = {};
115
+ adapters.forEach(adapter => {
116
+ counts[adapter.id] = adapter.getEntryCount();
117
+ });
118
+ return counts;
119
+ }, [adapters, refreshKey]);
120
+ const value = useMemo(() => ({
121
+ adapters,
122
+ getAdapter,
123
+ registerAdapter,
124
+ unregisterAdapter,
125
+ getAllEntries,
126
+ getEntryCounts,
127
+ selectedAdapterId,
128
+ setSelectedAdapterId,
129
+ refresh
130
+ }), [adapters, getAdapter, registerAdapter, unregisterAdapter, getAllEntries, getEntryCounts, selectedAdapterId, refresh]);
131
+ return /*#__PURE__*/_jsx(HistoryContext.Provider, {
132
+ value: value,
133
+ children: children
134
+ });
135
+ }
136
+
137
+ // =============================================================================
138
+ // HOOKS
139
+ // =============================================================================
140
+
141
+ /**
142
+ * Get the full history context
143
+ */
144
+ export function useHistoryContext() {
145
+ const context = useContext(HistoryContext);
146
+ if (!context) {
147
+ throw new Error("useHistoryContext must be used within a HistoryProvider");
148
+ }
149
+ return context;
150
+ }
151
+
152
+ /**
153
+ * Get all registered adapters
154
+ */
155
+ export function useHistoryAdapters() {
156
+ return useHistoryContext().adapters;
157
+ }
158
+
159
+ /**
160
+ * Get a specific adapter by id
161
+ */
162
+ export function useHistoryAdapter(id) {
163
+ return useHistoryContext().getAdapter(id);
164
+ }
165
+
166
+ /**
167
+ * Get combined entries from all adapters
168
+ */
169
+ export function useAllHistoryEntries(filter) {
170
+ const {
171
+ getAllEntries
172
+ } = useHistoryContext();
173
+ return getAllEntries(filter);
174
+ }
175
+
176
+ /**
177
+ * Get entry counts per source
178
+ */
179
+ export function useHistoryEntryCounts() {
180
+ return useHistoryContext().getEntryCounts();
181
+ }
182
+
183
+ // =============================================================================
184
+ // FILTER HELPER
185
+ // =============================================================================
186
+
187
+ function applyFilter(entries, filter) {
188
+ return entries.filter(entry => {
189
+ // Search text
190
+ if (filter.searchText) {
191
+ const search = filter.searchText.toLowerCase();
192
+ if (!entry.label.toLowerCase().includes(search)) {
193
+ return false;
194
+ }
195
+ }
196
+
197
+ // Only with changes
198
+ if (filter.onlyWithChanges && !entry.hasStateChange) {
199
+ return false;
200
+ }
201
+
202
+ // Only errors
203
+ if (filter.onlyErrors && entry.status !== "error") {
204
+ return false;
205
+ }
206
+
207
+ // Time range
208
+ if (filter.afterTimestamp && entry.timestamp < filter.afterTimestamp) {
209
+ return false;
210
+ }
211
+ if (filter.beforeTimestamp && entry.timestamp > filter.beforeTimestamp) {
212
+ return false;
213
+ }
214
+
215
+ // Ignored patterns
216
+ if (filter.ignoredPatterns?.length) {
217
+ for (const pattern of filter.ignoredPatterns) {
218
+ if (new RegExp(pattern).test(entry.label)) {
219
+ return false;
220
+ }
221
+ }
222
+ }
223
+
224
+ // Included patterns (must match at least one)
225
+ if (filter.includedPatterns?.length) {
226
+ let matched = false;
227
+ for (const pattern of filter.includedPatterns) {
228
+ if (new RegExp(pattern).test(entry.label)) {
229
+ matched = true;
230
+ break;
231
+ }
232
+ }
233
+ if (!matched) return false;
234
+ }
235
+ return true;
236
+ });
237
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * HistoryEntryRow - Universal row component for history entries
5
+ *
6
+ * Wraps CompactRow to display entries from any adapter (Redux, Zustand, MMKV, etc.)
7
+ */
8
+
9
+ import { View, Text, StyleSheet } from "react-native";
10
+ import { CompactRow } from "../../ui/components/CompactRow.js";
11
+ import { buoyColors } from "../../ui/gameUI/constants/gameUIColors.js";
12
+ import { jsx as _jsx } from "react/jsx-runtime";
13
+ /**
14
+ * Format timestamp to relative time
15
+ */
16
+ function formatRelativeTime(timestamp) {
17
+ const now = Date.now();
18
+ const diff = now - timestamp;
19
+ if (diff < 1000) return "just now";
20
+ if (diff < 60000) return `${Math.floor(diff / 1000)}s ago`;
21
+ if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
22
+ return `${Math.floor(diff / 3600000)}h ago`;
23
+ }
24
+
25
+ /**
26
+ * Get status color based on entry properties
27
+ */
28
+ function getStatusColor(entry, adapter) {
29
+ // Use adapter color if available
30
+ if (adapter?.color) {
31
+ return adapter.color;
32
+ }
33
+
34
+ // Status-based colors (React Query style)
35
+ if (entry.status === "error") return buoyColors.error;
36
+ if (entry.status === "pending") return buoyColors.warning;
37
+ if (entry.status === "success") return buoyColors.success;
38
+
39
+ // Change-based colors
40
+ if (entry.hasStateChange) return buoyColors.success;
41
+
42
+ // Skipped entries
43
+ if (entry.isSkipped) return buoyColors.textMuted;
44
+
45
+ // Future entries (after current index)
46
+ if (entry.isInFuture) return buoyColors.textMuted + "80";
47
+
48
+ // Source-based fallback colors
49
+ switch (entry.source) {
50
+ case "redux":
51
+ return "#764ABC";
52
+ // Redux purple
53
+ case "zustand":
54
+ return "#443E38";
55
+ // Zustand brown
56
+ case "mmkv":
57
+ return "#4CAF50";
58
+ // Green
59
+ case "async-storage":
60
+ return "#2196F3";
61
+ // Blue
62
+ case "react-query":
63
+ return "#FF4154";
64
+ // React Query red
65
+ case "jotai":
66
+ return "#000000";
67
+ // Black
68
+ case "legend-state":
69
+ return "#7C3AED";
70
+ // Purple
71
+ default:
72
+ return buoyColors.textSecondary;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Get status label for display
78
+ */
79
+ function getStatusLabel(entry) {
80
+ if (entry.isSkipped) return "Skipped";
81
+ if (entry.isInFuture) return "Future";
82
+ if (entry.status) {
83
+ return entry.status.charAt(0).toUpperCase() + entry.status.slice(1);
84
+ }
85
+ if (entry.hasStateChange) return "Changed";
86
+ return "Action";
87
+ }
88
+
89
+ /**
90
+ * Get sublabel (source or category)
91
+ */
92
+ function getSublabel(entry, adapter) {
93
+ if (adapter?.name) return adapter.name;
94
+ if (entry.category) return entry.category;
95
+ return entry.source;
96
+ }
97
+ export function HistoryEntryRow({
98
+ entry,
99
+ adapter,
100
+ isSelected,
101
+ onPress,
102
+ showSource = true,
103
+ showDuration = true
104
+ }) {
105
+ const statusColor = getStatusColor(entry, adapter);
106
+ const statusLabel = getStatusLabel(entry);
107
+ const sublabel = showSource ? getSublabel(entry, adapter) : undefined;
108
+
109
+ // Build bottom right text (timestamp + optional duration)
110
+ let bottomRightText = formatRelativeTime(entry.timestamp);
111
+ if (showDuration && entry.duration !== undefined) {
112
+ bottomRightText = `${entry.duration.toFixed(1)}ms · ${bottomRightText}`;
113
+ }
114
+
115
+ // Custom badge showing change indicator
116
+ const customBadge = entry.hasStateChange ? /*#__PURE__*/_jsx(View, {
117
+ style: styles.changeBadge,
118
+ children: /*#__PURE__*/_jsx(Text, {
119
+ style: styles.changeBadgeText,
120
+ children: "\u26A1"
121
+ })
122
+ }) : undefined;
123
+ return /*#__PURE__*/_jsx(CompactRow, {
124
+ statusDotColor: statusColor,
125
+ statusLabel: statusLabel,
126
+ statusSublabel: sublabel,
127
+ primaryText: entry.label,
128
+ bottomRightText: bottomRightText,
129
+ customBadge: customBadge,
130
+ isSelected: isSelected,
131
+ onPress: onPress ? () => onPress(entry) : undefined,
132
+ showChevron: !!onPress
133
+ });
134
+ }
135
+ const styles = StyleSheet.create({
136
+ changeBadge: {
137
+ paddingHorizontal: 4
138
+ },
139
+ changeBadgeText: {
140
+ fontSize: 12
141
+ }
142
+ });
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * HistoryList - Virtualized list of history entries
5
+ *
6
+ * Displays entries from one or all adapters with filtering and search.
7
+ */
8
+
9
+ import { useCallback, useMemo } from "react";
10
+ import { FlatList, View, Text, StyleSheet } from "react-native";
11
+ import { buoyColors } from "../../ui/gameUI/constants/gameUIColors.js";
12
+ import { HistoryEntryRow } from "./HistoryEntryRow.js";
13
+ import { useAllHistoryEntries, useHistoryAdapters } from "../HistoryProvider.js";
14
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ export function HistoryList({
16
+ filter,
17
+ selectedEntryId,
18
+ onSelectEntry,
19
+ showSource = true,
20
+ emptyMessage = "No history entries yet",
21
+ ListHeaderComponent,
22
+ ListFooterComponent
23
+ }) {
24
+ const entries = useAllHistoryEntries(filter);
25
+ const adapters = useHistoryAdapters();
26
+
27
+ // Create adapter lookup map
28
+ const adapterMap = useMemo(() => {
29
+ const map = new Map();
30
+ adapters.forEach(adapter => map.set(adapter.id, adapter));
31
+ return map;
32
+ }, [adapters]);
33
+
34
+ // Stable key extractor
35
+ const keyExtractor = useCallback(item => `${item.source}-${item.id}`, []);
36
+
37
+ // Render item
38
+ const renderItem = useCallback(({
39
+ item
40
+ }) => {
41
+ const adapter = adapterMap.get(item.source);
42
+ return /*#__PURE__*/_jsx(HistoryEntryRow, {
43
+ entry: item,
44
+ adapter: adapter,
45
+ isSelected: selectedEntryId === item.id,
46
+ onPress: onSelectEntry,
47
+ showSource: showSource
48
+ });
49
+ }, [adapterMap, selectedEntryId, onSelectEntry, showSource]);
50
+
51
+ // Empty state
52
+ const ListEmptyComponent = useMemo(() => /*#__PURE__*/_jsxs(View, {
53
+ style: styles.emptyContainer,
54
+ children: [/*#__PURE__*/_jsx(Text, {
55
+ style: styles.emptyText,
56
+ children: emptyMessage
57
+ }), /*#__PURE__*/_jsx(Text, {
58
+ style: styles.emptySubtext,
59
+ children: "Actions will appear here as they happen"
60
+ })]
61
+ }), [emptyMessage]);
62
+ return /*#__PURE__*/_jsx(FlatList, {
63
+ data: entries,
64
+ keyExtractor: keyExtractor,
65
+ renderItem: renderItem,
66
+ ListEmptyComponent: ListEmptyComponent,
67
+ ListHeaderComponent: ListHeaderComponent,
68
+ ListFooterComponent: ListFooterComponent,
69
+ contentContainerStyle: styles.listContent,
70
+ showsVerticalScrollIndicator: false
71
+ // Performance optimizations
72
+ ,
73
+ removeClippedSubviews: true,
74
+ maxToRenderPerBatch: 15,
75
+ windowSize: 10,
76
+ initialNumToRender: 15,
77
+ getItemLayout: undefined // Dynamic height rows
78
+ });
79
+ }
80
+
81
+ /**
82
+ * Standalone HistoryList that doesn't require HistoryProvider
83
+ * Use this when you want to pass entries directly
84
+ */
85
+
86
+ export function StandaloneHistoryList({
87
+ entries,
88
+ adapters = [],
89
+ selectedEntryId,
90
+ onSelectEntry,
91
+ showSource = true,
92
+ emptyMessage = "No history entries yet",
93
+ ListHeaderComponent,
94
+ ListFooterComponent
95
+ }) {
96
+ // Create adapter lookup map
97
+ const adapterMap = useMemo(() => {
98
+ const map = new Map();
99
+ adapters.forEach(adapter => map.set(adapter.id, adapter));
100
+ return map;
101
+ }, [adapters]);
102
+
103
+ // Stable key extractor
104
+ const keyExtractor = useCallback(item => `${item.source}-${item.id}`, []);
105
+
106
+ // Render item
107
+ const renderItem = useCallback(({
108
+ item
109
+ }) => {
110
+ const adapter = adapterMap.get(item.source);
111
+ return /*#__PURE__*/_jsx(HistoryEntryRow, {
112
+ entry: item,
113
+ adapter: adapter,
114
+ isSelected: selectedEntryId === item.id,
115
+ onPress: onSelectEntry,
116
+ showSource: showSource
117
+ });
118
+ }, [adapterMap, selectedEntryId, onSelectEntry, showSource]);
119
+
120
+ // Empty state
121
+ const ListEmptyComponent = useMemo(() => /*#__PURE__*/_jsxs(View, {
122
+ style: styles.emptyContainer,
123
+ children: [/*#__PURE__*/_jsx(Text, {
124
+ style: styles.emptyText,
125
+ children: emptyMessage
126
+ }), /*#__PURE__*/_jsx(Text, {
127
+ style: styles.emptySubtext,
128
+ children: "Actions will appear here as they happen"
129
+ })]
130
+ }), [emptyMessage]);
131
+ return /*#__PURE__*/_jsx(FlatList, {
132
+ data: entries,
133
+ keyExtractor: keyExtractor,
134
+ renderItem: renderItem,
135
+ ListEmptyComponent: ListEmptyComponent,
136
+ ListHeaderComponent: ListHeaderComponent,
137
+ ListFooterComponent: ListFooterComponent,
138
+ contentContainerStyle: styles.listContent,
139
+ showsVerticalScrollIndicator: false,
140
+ removeClippedSubviews: true,
141
+ maxToRenderPerBatch: 15,
142
+ windowSize: 10,
143
+ initialNumToRender: 15
144
+ });
145
+ }
146
+ const styles = StyleSheet.create({
147
+ listContent: {
148
+ flexGrow: 1,
149
+ paddingVertical: 4
150
+ },
151
+ emptyContainer: {
152
+ flex: 1,
153
+ alignItems: "center",
154
+ justifyContent: "center",
155
+ paddingVertical: 60,
156
+ paddingHorizontal: 20
157
+ },
158
+ emptyText: {
159
+ fontSize: 16,
160
+ fontWeight: "600",
161
+ color: buoyColors.text,
162
+ marginBottom: 8
163
+ },
164
+ emptySubtext: {
165
+ fontSize: 13,
166
+ color: buoyColors.textMuted,
167
+ textAlign: "center"
168
+ }
169
+ });
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Universal History DevTools Components
5
+ */
6
+
7
+ export { HistoryEntryRow } from "./HistoryEntryRow.js";
8
+ export { HistoryList, StandaloneHistoryList } from "./HistoryList.js";
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Universal History DevTools
5
+ *
6
+ * A unified devtools experience for Redux, Zustand, MMKV, React Query, and more.
7
+ */
8
+
9
+ // Types (IconComponent is already exported from ui)
10
+
11
+ // Provider & Hooks
12
+ export { HistoryProvider, useHistoryContext, useHistoryAdapters, useHistoryAdapter, useAllHistoryEntries, useHistoryEntryCounts } from "./HistoryProvider.js";
13
+
14
+ // Components
15
+ export { HistoryEntryRow, HistoryList, StandaloneHistoryList } from "./components/index.js";
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+
3
+ export {};
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * Auto-generated safe area implementation
5
5
  * Detected: none
6
- * Generated at: 2026-01-03T18:51:49.025Z
6
+ * Generated at: 2026-01-12T04:02:16.222Z
7
7
  *
8
8
  * DO NOT EDIT - This file is generated by scripts/detect-safe-area.js
9
9
  *