@buoy-gg/zustand 2.1.12 → 2.1.13

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 (56) hide show
  1. package/lib/commonjs/index.js +1 -91
  2. package/lib/commonjs/preset.js +1 -102
  3. package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -116
  4. package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +1 -134
  5. package/lib/commonjs/zustand/components/ZustandEventFilterView.js +1 -291
  6. package/lib/commonjs/zustand/components/ZustandIcon.js +1 -35
  7. package/lib/commonjs/zustand/components/ZustandModal.js +1 -603
  8. package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +1 -165
  9. package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +1 -352
  10. package/lib/commonjs/zustand/components/ZustandStateInfoView.js +1 -508
  11. package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +1 -307
  12. package/lib/commonjs/zustand/components/index.js +1 -73
  13. package/lib/commonjs/zustand/hooks/index.js +1 -12
  14. package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +1 -92
  15. package/lib/commonjs/zustand/index.js +1 -99
  16. package/lib/commonjs/zustand/utils/buoyZustandMiddleware.js +1 -220
  17. package/lib/commonjs/zustand/utils/index.js +1 -31
  18. package/lib/commonjs/zustand/utils/zustandStateStore.js +1 -361
  19. package/lib/module/index.js +1 -80
  20. package/lib/module/preset.js +1 -98
  21. package/lib/module/zustand/components/ZustandActionButton.js +1 -112
  22. package/lib/module/zustand/components/ZustandDetailViewToggle.js +1 -129
  23. package/lib/module/zustand/components/ZustandEventFilterView.js +1 -287
  24. package/lib/module/zustand/components/ZustandIcon.js +1 -32
  25. package/lib/module/zustand/components/ZustandModal.js +1 -599
  26. package/lib/module/zustand/components/ZustandStateChangeItem.js +1 -161
  27. package/lib/module/zustand/components/ZustandStateDetailContent.js +1 -348
  28. package/lib/module/zustand/components/ZustandStateInfoView.js +1 -503
  29. package/lib/module/zustand/components/ZustandStoreBrowser.js +1 -303
  30. package/lib/module/zustand/components/index.js +1 -10
  31. package/lib/module/zustand/hooks/index.js +1 -3
  32. package/lib/module/zustand/hooks/useZustandStateChanges.js +1 -88
  33. package/lib/module/zustand/index.js +1 -12
  34. package/lib/module/zustand/utils/buoyZustandMiddleware.js +1 -214
  35. package/lib/module/zustand/utils/index.js +1 -4
  36. package/lib/module/zustand/utils/zustandStateStore.js +1 -357
  37. package/package.json +3 -3
  38. package/lib/typescript/index.d.ts.map +0 -1
  39. package/lib/typescript/preset.d.ts.map +0 -1
  40. package/lib/typescript/zustand/components/ZustandActionButton.d.ts.map +0 -1
  41. package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +0 -1
  42. package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +0 -1
  43. package/lib/typescript/zustand/components/ZustandIcon.d.ts.map +0 -1
  44. package/lib/typescript/zustand/components/ZustandModal.d.ts.map +0 -1
  45. package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +0 -1
  46. package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +0 -1
  47. package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +0 -1
  48. package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +0 -1
  49. package/lib/typescript/zustand/components/index.d.ts.map +0 -1
  50. package/lib/typescript/zustand/hooks/index.d.ts.map +0 -1
  51. package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +0 -1
  52. package/lib/typescript/zustand/index.d.ts.map +0 -1
  53. package/lib/typescript/zustand/types/index.d.ts.map +0 -1
  54. package/lib/typescript/zustand/utils/buoyZustandMiddleware.d.ts.map +0 -1
  55. package/lib/typescript/zustand/utils/index.d.ts.map +0 -1
  56. package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +0 -1
@@ -1,599 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Main Zustand DevTools modal
5
- *
6
- * Two tabs mirroring the Storage DevTools pattern:
7
- * - Stores tab: Browse all registered stores and their current state
8
- * - Events tab: Live state change monitoring with diffs
9
- */
10
-
11
- import { useState, useMemo, useRef, useCallback } from "react";
12
- import { View, Text, StyleSheet, FlatList, TextInput, TouchableOpacity } from "react-native";
13
- import { JsModal, ModalHeader, TabSelector, macOSColors, buoyColors, Search, Filter, Power, X, Lock, Box, devToolsStorageKeys, ProUpgradeModal, PowerToggleButton, ToolbarCopyButton, ToolbarClearButton, truncatePayload, TickProvider } from "@buoy-gg/shared-ui";
14
- import { useIsPro } from "@buoy-gg/license";
15
- import { useZustandStateChanges } from "../hooks/useZustandStateChanges";
16
- import { ZustandStateChangeItem } from "./ZustandStateChangeItem";
17
- import { ZustandStateDetailContent, ZustandStateDetailFooter } from "./ZustandStateDetailContent";
18
- import { ZustandStoreBrowser } from "./ZustandStoreBrowser";
19
- import { ZustandEventFilterView } from "./ZustandEventFilterView";
20
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
21
- /** Free tier limit for change history */
22
- const FREE_TIER_CHANGE_LIMIT = 25;
23
- function EventsEmptyState({
24
- isEnabled
25
- }) {
26
- return /*#__PURE__*/_jsxs(View, {
27
- style: styles.emptyState,
28
- children: [/*#__PURE__*/_jsx(Box, {
29
- size: 32,
30
- color: macOSColors.text.muted
31
- }), /*#__PURE__*/_jsx(Text, {
32
- style: styles.emptyTitle,
33
- children: "No state changes"
34
- }), /*#__PURE__*/_jsx(Text, {
35
- style: styles.emptyText,
36
- children: isEnabled ? "State changes will appear here as stores update." : "Enable capture to start recording state changes"
37
- })]
38
- });
39
- }
40
- export function ZustandModal({
41
- visible,
42
- onClose,
43
- onBack,
44
- onMinimize,
45
- enableSharedModalDimensions = false
46
- }) {
47
- const isPro = useIsPro();
48
- const [showProModal, setShowProModal] = useState(false);
49
- const [activeTab, setActiveTab] = useState("stores");
50
- const [selectedHistoryStore, setSelectedHistoryStore] = useState(null);
51
- const [showFilters, setShowFilters] = useState(false);
52
- const [ignoredPatterns, setIgnoredPatterns] = useState(new Set());
53
- const {
54
- filteredChanges,
55
- filter,
56
- setFilter,
57
- stats,
58
- stores,
59
- clearChanges,
60
- isEnabled,
61
- toggleCapture
62
- } = useZustandStateChanges();
63
-
64
- // Apply ignored patterns on top of hook's filteredChanges
65
- const displayedChanges = useMemo(() => {
66
- if (ignoredPatterns.size === 0) return filteredChanges;
67
- return filteredChanges.filter(c => !Array.from(ignoredPatterns).some(p => c.storeName.toLowerCase().includes(p.toLowerCase())));
68
- }, [filteredChanges, ignoredPatterns]);
69
-
70
- // Apply ignored patterns to stores list too
71
- const displayedStores = useMemo(() => {
72
- if (ignoredPatterns.size === 0) return stores;
73
- return stores.filter(s => !Array.from(ignoredPatterns).some(p => s.name.toLowerCase().includes(p.toLowerCase())));
74
- }, [stores, ignoredPatterns]);
75
-
76
- // For free users, limit visible changes
77
- const visibleChanges = useMemo(() => {
78
- if (isPro) return displayedChanges;
79
- return displayedChanges.slice(0, FREE_TIER_CHANGE_LIMIT);
80
- }, [displayedChanges, isPro]);
81
- const lockedChangeCount = useMemo(() => {
82
- if (isPro) return 0;
83
- return Math.max(0, displayedChanges.length - FREE_TIER_CHANGE_LIMIT);
84
- }, [displayedChanges.length, isPro]);
85
- const hasLockedChanges = lockedChangeCount > 0;
86
-
87
- // When viewing a single store's history, scope the change list to that store
88
- const activeChanges = useMemo(() => {
89
- if (selectedHistoryStore) {
90
- return visibleChanges.filter(c => c.storeName === selectedHistoryStore);
91
- }
92
- return visibleChanges;
93
- }, [visibleChanges, selectedHistoryStore]);
94
-
95
- // Selection state — track by ID so new events don't shift position
96
- const [selectedChangeId, setSelectedChangeId] = useState(null);
97
- const selectedChangeIndex = useMemo(() => {
98
- if (selectedChangeId === null) return null;
99
- const idx = activeChanges.findIndex(c => c.id === selectedChangeId);
100
- return idx >= 0 ? idx : null;
101
- }, [selectedChangeId, activeChanges]);
102
- const selectedChange = selectedChangeIndex !== null ? activeChanges[selectedChangeIndex] : null;
103
-
104
- // Search state — shared across tabs
105
- const [searchText, setSearchText] = useState("");
106
- const [isSearchActive, setIsSearchActive] = useState(false);
107
- const searchInputRef = useRef(null);
108
- const flatListRef = useRef(null);
109
- const handleModeChange = useCallback(_mode => {}, []);
110
- const handleSearch = text => {
111
- setSearchText(text);
112
- if (activeTab === "events") {
113
- setFilter(prev => ({
114
- ...prev,
115
- searchText: text
116
- }));
117
- }
118
- };
119
- const handleChangePress = useCallback(change => {
120
- setSelectedChangeId(change.id);
121
- }, []);
122
- const handleUpgradePress = useCallback(() => {
123
- setShowProModal(true);
124
- }, []);
125
- const handleBack = useCallback(() => {
126
- setSelectedChangeId(null);
127
- }, []);
128
- const handleIndexChange = useCallback(newIndex => {
129
- const change = activeChanges[newIndex];
130
- if (change) {
131
- setSelectedChangeId(change.id);
132
- }
133
- }, [activeChanges]);
134
- const handleViewHistory = useCallback(storeName => {
135
- setSelectedHistoryStore(storeName);
136
- setSelectedChangeId(null);
137
- }, []);
138
- const handleBackFromHistory = useCallback(() => {
139
- setSelectedHistoryStore(null);
140
- setSelectedChangeId(null);
141
- }, []);
142
- const getStoresSnapshot = useCallback(() => {
143
- return displayedStores.reduce((acc, store) => {
144
- try {
145
- const state = store.api.getState();
146
- if (state && typeof state === "object") {
147
- const filtered = {};
148
- for (const [key, value] of Object.entries(state)) {
149
- if (typeof value !== "function") filtered[key] = truncatePayload(value);
150
- }
151
- acc[store.name] = filtered;
152
- } else {
153
- acc[store.name] = truncatePayload(state);
154
- }
155
- } catch {
156
- acc[store.name] = null;
157
- }
158
- return acc;
159
- }, {});
160
- }, [displayedStores]);
161
- const getEventsSnapshot = useCallback(() => {
162
- return displayedChanges.map(change => ({
163
- id: change.id,
164
- storeName: change.storeName,
165
- timestamp: change.timestamp,
166
- prevState: truncatePayload(change.prevState),
167
- nextState: truncatePayload(change.nextState),
168
- hasStateChange: change.hasStateChange,
169
- changedKeys: change.changedKeys
170
- }));
171
- }, [displayedChanges]);
172
- const getHistorySnapshot = useCallback(() => {
173
- return activeChanges.map(change => ({
174
- id: change.id,
175
- storeName: change.storeName,
176
- timestamp: change.timestamp,
177
- prevState: truncatePayload(change.prevState),
178
- nextState: truncatePayload(change.nextState),
179
- hasStateChange: change.hasStateChange,
180
- changedKeys: change.changedKeys
181
- }));
182
- }, [activeChanges]);
183
- const handleTogglePattern = useCallback(pattern => {
184
- setIgnoredPatterns(prev => {
185
- const next = new Set(prev);
186
- if (next.has(pattern)) next.delete(pattern);else next.add(pattern);
187
- return next;
188
- });
189
- }, []);
190
- const handleAddPattern = useCallback(pattern => {
191
- setIgnoredPatterns(prev => new Set([...prev, pattern]));
192
- }, []);
193
- const handleTabChange = useCallback(tab => {
194
- setActiveTab(tab);
195
- setSelectedHistoryStore(null);
196
- setSelectedChangeId(null);
197
- setShowFilters(false);
198
- if (searchText) {
199
- setSearchText("");
200
- setFilter(prev => ({
201
- ...prev,
202
- searchText: ""
203
- }));
204
- }
205
- setIsSearchActive(false);
206
- }, [searchText, setFilter]);
207
- const keyExtractor = useCallback(item => item.id, []);
208
- const renderItem = useCallback(({
209
- item
210
- }) => {
211
- return /*#__PURE__*/_jsx(ZustandStateChangeItem, {
212
- change: item,
213
- onPress: handleChangePress
214
- });
215
- }, [handleChangePress]);
216
-
217
- // ---- Header rendering ----
218
-
219
- const hasActiveFilters = ignoredPatterns.size > 0;
220
- const renderHeaderContent = () => {
221
- // Filter view header
222
- if (showFilters) {
223
- return /*#__PURE__*/_jsxs(ModalHeader, {
224
- children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
225
- onBack: () => setShowFilters(false)
226
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
227
- title: "Filters",
228
- centered: true
229
- })]
230
- });
231
- }
232
-
233
- // Change detail header
234
- if (selectedChange) {
235
- return /*#__PURE__*/_jsxs(ModalHeader, {
236
- children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
237
- onBack: handleBack
238
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
239
- title: `${selectedChange.storeName}/setState`,
240
- centered: true
241
- })]
242
- });
243
- }
244
-
245
- // Store history header
246
- if (selectedHistoryStore) {
247
- return /*#__PURE__*/_jsxs(ModalHeader, {
248
- children: [/*#__PURE__*/_jsx(ModalHeader.Navigation, {
249
- onBack: handleBackFromHistory
250
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
251
- title: `${selectedHistoryStore} History`,
252
- centered: true
253
- }), /*#__PURE__*/_jsx(ModalHeader.Actions, {
254
- children: /*#__PURE__*/_jsx(ToolbarCopyButton, {
255
- value: getHistorySnapshot,
256
- disabled: activeChanges.length === 0,
257
- buttonStyle: styles.headerActionButton
258
- })
259
- })]
260
- });
261
- }
262
-
263
- // Main view with tabs
264
- return /*#__PURE__*/_jsxs(ModalHeader, {
265
- children: [onBack && /*#__PURE__*/_jsx(ModalHeader.Navigation, {
266
- onBack: onBack
267
- }), /*#__PURE__*/_jsx(ModalHeader.Content, {
268
- title: "",
269
- children: isSearchActive ? /*#__PURE__*/_jsxs(View, {
270
- style: styles.headerSearchContainer,
271
- children: [/*#__PURE__*/_jsx(Search, {
272
- size: 14,
273
- color: macOSColors.text.secondary
274
- }), /*#__PURE__*/_jsx(TextInput, {
275
- ref: searchInputRef,
276
- style: styles.headerSearchInput,
277
- placeholder: activeTab === "stores" ? "Search stores..." : "Search events...",
278
- placeholderTextColor: macOSColors.text.muted,
279
- value: searchText,
280
- onChangeText: handleSearch,
281
- onSubmitEditing: () => setIsSearchActive(false),
282
- onBlur: () => setIsSearchActive(false),
283
- autoCapitalize: "none",
284
- autoCorrect: false,
285
- returnKeyType: "search"
286
- }), searchText.length > 0 && /*#__PURE__*/_jsx(TouchableOpacity, {
287
- onPress: () => {
288
- handleSearch("");
289
- setIsSearchActive(false);
290
- },
291
- style: styles.headerSearchClear,
292
- children: /*#__PURE__*/_jsx(X, {
293
- size: 14,
294
- color: macOSColors.text.secondary
295
- })
296
- })]
297
- }) : /*#__PURE__*/_jsx(TabSelector, {
298
- tabs: [{
299
- key: "stores",
300
- label: `Stores${displayedStores.length > 0 ? ` (${displayedStores.length})` : ""}`
301
- }, {
302
- key: "events",
303
- label: `Events${displayedChanges.length > 0 ? ` (${displayedChanges.length})` : ""}`
304
- }],
305
- activeTab: activeTab,
306
- onTabChange: handleTabChange
307
- })
308
- }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
309
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
310
- onPress: () => setIsSearchActive(true),
311
- style: styles.headerActionButton,
312
- children: /*#__PURE__*/_jsx(Search, {
313
- size: 14,
314
- color: macOSColors.text.secondary
315
- })
316
- }), /*#__PURE__*/_jsx(TouchableOpacity, {
317
- onPress: () => setShowFilters(true),
318
- style: [styles.headerActionButton, hasActiveFilters && styles.headerActionButtonActive],
319
- children: /*#__PURE__*/_jsx(Filter, {
320
- size: 14,
321
- color: hasActiveFilters ? macOSColors.semantic.debug : macOSColors.text.secondary
322
- })
323
- }), activeTab === "stores" && /*#__PURE__*/_jsx(ToolbarCopyButton, {
324
- value: getStoresSnapshot,
325
- disabled: displayedStores.length === 0,
326
- buttonStyle: styles.headerActionButton
327
- }), activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
328
- children: [/*#__PURE__*/_jsx(ToolbarCopyButton, {
329
- value: getEventsSnapshot,
330
- disabled: displayedChanges.length === 0,
331
- buttonStyle: styles.headerActionButton
332
- }), /*#__PURE__*/_jsx(PowerToggleButton, {
333
- isEnabled: isEnabled,
334
- onToggle: toggleCapture,
335
- accessibilityLabel: "Toggle Zustand state capture"
336
- }), /*#__PURE__*/_jsx(ToolbarClearButton, {
337
- onPress: clearChanges,
338
- disabled: displayedChanges.length === 0,
339
- buttonStyle: styles.headerActionButton
340
- })]
341
- })]
342
- })]
343
- });
344
- };
345
-
346
- // ---- Content rendering ----
347
-
348
- const renderContent = () => {
349
- // Filter view (applies to both tabs)
350
- if (showFilters) {
351
- return /*#__PURE__*/_jsx(ZustandEventFilterView, {
352
- ignoredPatterns: ignoredPatterns,
353
- onTogglePattern: handleTogglePattern,
354
- onAddPattern: handleAddPattern,
355
- stores: stores
356
- });
357
- }
358
-
359
- // Change detail view (works for both events tab and store history)
360
- if (selectedChange && selectedChangeIndex !== null) {
361
- return /*#__PURE__*/_jsx(ZustandStateDetailContent, {
362
- change: selectedChange,
363
- changes: activeChanges,
364
- selectedIndex: selectedChangeIndex,
365
- onIndexChange: handleIndexChange,
366
- disableInternalFooter: true
367
- });
368
- }
369
-
370
- // Store history view — filtered to one store
371
- if (selectedHistoryStore) {
372
- return activeChanges.length > 0 ? /*#__PURE__*/_jsx(FlatList, {
373
- data: activeChanges,
374
- renderItem: renderItem,
375
- keyExtractor: keyExtractor,
376
- contentContainerStyle: styles.listContent,
377
- showsVerticalScrollIndicator: true,
378
- removeClippedSubviews: true,
379
- initialNumToRender: 15,
380
- maxToRenderPerBatch: 10,
381
- windowSize: 10,
382
- scrollEnabled: false
383
- }) : /*#__PURE__*/_jsxs(View, {
384
- style: styles.emptyState,
385
- children: [/*#__PURE__*/_jsx(Box, {
386
- size: 32,
387
- color: macOSColors.text.muted
388
- }), /*#__PURE__*/_jsx(Text, {
389
- style: styles.emptyTitle,
390
- children: "No history yet"
391
- }), /*#__PURE__*/_jsxs(Text, {
392
- style: styles.emptyText,
393
- children: ["State changes for ", selectedHistoryStore, " will appear here."]
394
- })]
395
- });
396
- }
397
-
398
- // Stores browse tab
399
- if (activeTab === "stores") {
400
- return /*#__PURE__*/_jsx(ZustandStoreBrowser, {
401
- stores: displayedStores,
402
- searchQuery: searchText,
403
- onViewHistory: handleViewHistory
404
- });
405
- }
406
-
407
- // Events tab
408
- return /*#__PURE__*/_jsxs(_Fragment, {
409
- children: [!isEnabled && /*#__PURE__*/_jsxs(View, {
410
- style: styles.disabledBanner,
411
- children: [/*#__PURE__*/_jsx(Power, {
412
- size: 14,
413
- color: macOSColors.semantic.warning
414
- }), /*#__PURE__*/_jsx(Text, {
415
- style: styles.disabledText,
416
- children: "State capture is disabled"
417
- })]
418
- }), hasLockedChanges && /*#__PURE__*/_jsxs(TouchableOpacity, {
419
- style: styles.lockedBanner,
420
- onPress: handleUpgradePress,
421
- activeOpacity: 0.8,
422
- children: [/*#__PURE__*/_jsx(Lock, {
423
- size: 14,
424
- color: buoyColors.primary
425
- }), /*#__PURE__*/_jsxs(Text, {
426
- style: styles.lockedText,
427
- children: [lockedChangeCount, " older", " ", lockedChangeCount === 1 ? "change" : "changes", " locked"]
428
- }), /*#__PURE__*/_jsx(View, {
429
- style: styles.upgradeBadge,
430
- children: /*#__PURE__*/_jsx(Text, {
431
- style: styles.upgradeBadgeText,
432
- children: "UPGRADE"
433
- })
434
- })]
435
- }), visibleChanges.length > 0 ? /*#__PURE__*/_jsx(FlatList, {
436
- ref: flatListRef,
437
- data: visibleChanges,
438
- renderItem: renderItem,
439
- keyExtractor: keyExtractor,
440
- contentContainerStyle: styles.listContent,
441
- showsVerticalScrollIndicator: true,
442
- removeClippedSubviews: true,
443
- initialNumToRender: 15,
444
- maxToRenderPerBatch: 10,
445
- windowSize: 10,
446
- scrollEnabled: false
447
- }) : /*#__PURE__*/_jsx(EventsEmptyState, {
448
- isEnabled: isEnabled
449
- })]
450
- });
451
- };
452
- const persistenceKey = enableSharedModalDimensions ? devToolsStorageKeys.modal.root() : "buoy-zustand-modal";
453
- if (!visible) return null;
454
- const footerNode = selectedChange && selectedChangeIndex !== null ? /*#__PURE__*/_jsx(ZustandStateDetailFooter, {
455
- change: selectedChange,
456
- changes: activeChanges,
457
- selectedIndex: selectedChangeIndex,
458
- onIndexChange: handleIndexChange
459
- }) : null;
460
- const footerHeight = selectedChange && activeChanges.length > 1 ? 68 : 0;
461
- return /*#__PURE__*/_jsxs(TickProvider, {
462
- children: [/*#__PURE__*/_jsx(JsModal, {
463
- visible: visible,
464
- onClose: onClose,
465
- onMinimize: onMinimize,
466
- persistenceKey: persistenceKey,
467
- header: {
468
- showToggleButton: true,
469
- customContent: renderHeaderContent()
470
- },
471
- onModeChange: handleModeChange,
472
- enablePersistence: true,
473
- initialMode: "bottomSheet",
474
- enableGlitchEffects: true,
475
- styles: {},
476
- footer: footerNode,
477
- footerHeight: footerHeight,
478
- children: /*#__PURE__*/_jsx(View, {
479
- style: styles.container,
480
- children: renderContent()
481
- })
482
- }), /*#__PURE__*/_jsx(ProUpgradeModal, {
483
- visible: showProModal,
484
- onClose: () => setShowProModal(false),
485
- featureName: "Full State History"
486
- })]
487
- });
488
- }
489
- const styles = StyleSheet.create({
490
- container: {
491
- flex: 1,
492
- backgroundColor: macOSColors.background.base
493
- },
494
- headerSearchContainer: {
495
- flexDirection: "row",
496
- alignItems: "center",
497
- backgroundColor: macOSColors.background.input,
498
- borderRadius: 10,
499
- borderWidth: 1,
500
- borderColor: macOSColors.border.default,
501
- paddingHorizontal: 12,
502
- paddingVertical: 5
503
- },
504
- headerSearchInput: {
505
- flex: 1,
506
- color: macOSColors.text.primary,
507
- fontSize: 13,
508
- marginLeft: 6,
509
- paddingVertical: 2
510
- },
511
- headerSearchClear: {
512
- marginLeft: 6,
513
- padding: 4
514
- },
515
- headerActionButton: {
516
- width: 32,
517
- height: 32,
518
- borderRadius: 8,
519
- backgroundColor: macOSColors.background.hover,
520
- borderWidth: 1,
521
- borderColor: macOSColors.border.default,
522
- alignItems: "center",
523
- justifyContent: "center"
524
- },
525
- headerActionButtonDisabled: {
526
- opacity: 0.55
527
- },
528
- headerActionButtonActive: {
529
- backgroundColor: macOSColors.semantic.infoBackground
530
- },
531
- disabledBanner: {
532
- flexDirection: "row",
533
- alignItems: "center",
534
- gap: 8,
535
- padding: 10,
536
- marginHorizontal: 12,
537
- marginTop: 8,
538
- backgroundColor: macOSColors.semantic.warningBackground,
539
- borderRadius: 8,
540
- borderWidth: 1,
541
- borderColor: macOSColors.semantic.warning + "20"
542
- },
543
- disabledText: {
544
- color: macOSColors.semantic.warning,
545
- fontSize: 11,
546
- flex: 1
547
- },
548
- listContent: {
549
- paddingTop: 8,
550
- paddingBottom: 20
551
- },
552
- emptyState: {
553
- alignItems: "center",
554
- paddingVertical: 40
555
- },
556
- emptyTitle: {
557
- color: macOSColors.text.primary,
558
- fontSize: 14,
559
- fontWeight: "600",
560
- marginTop: 12,
561
- marginBottom: 6
562
- },
563
- emptyText: {
564
- color: macOSColors.text.muted,
565
- fontSize: 12,
566
- textAlign: "center",
567
- lineHeight: 18
568
- },
569
- lockedBanner: {
570
- flexDirection: "row",
571
- alignItems: "center",
572
- gap: 8,
573
- padding: 10,
574
- marginHorizontal: 12,
575
- marginTop: 8,
576
- backgroundColor: buoyColors.primary + "15",
577
- borderRadius: 8,
578
- borderWidth: 1,
579
- borderColor: buoyColors.primary + "33"
580
- },
581
- lockedText: {
582
- color: buoyColors.primary,
583
- fontSize: 11,
584
- fontWeight: "500",
585
- flex: 1
586
- },
587
- upgradeBadge: {
588
- backgroundColor: buoyColors.primary,
589
- paddingHorizontal: 8,
590
- paddingVertical: 3,
591
- borderRadius: 4
592
- },
593
- upgradeBadgeText: {
594
- color: "#fff",
595
- fontSize: 9,
596
- fontWeight: "700",
597
- letterSpacing: 0.5
598
- }
599
- });
1
+ "use strict";import{useState,useMemo,useRef,useCallback}from"react";import{View,Text,StyleSheet,FlatList,TextInput,TouchableOpacity}from"react-native";import{JsModal,ModalHeader,TabSelector,macOSColors,buoyColors,Search,Filter,Power,X,Lock,Box,devToolsStorageKeys,ProUpgradeModal,PowerToggleButton,ToolbarCopyButton,ToolbarClearButton,truncatePayload,TickProvider}from"@buoy-gg/shared-ui";import{useIsPro}from"@buoy-gg/license";import{useZustandStateChanges}from"../hooks/useZustandStateChanges";import{ZustandStateChangeItem}from"./ZustandStateChangeItem";import{ZustandStateDetailContent,ZustandStateDetailFooter}from"./ZustandStateDetailContent";import{ZustandStoreBrowser}from"./ZustandStoreBrowser";import{ZustandEventFilterView}from"./ZustandEventFilterView";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";const FREE_TIER_CHANGE_LIMIT=25;function EventsEmptyState({isEnabled:e}){return _jsxs(View,{style:styles.emptyState,children:[_jsx(Box,{size:32,color:macOSColors.text.muted}),_jsx(Text,{style:styles.emptyTitle,children:"No state changes"}),_jsx(Text,{style:styles.emptyText,children:e?"State changes will appear here as stores update.":"Enable capture to start recording state changes"})]})}export function ZustandModal({visible:e,onClose:t,onBack:a,onMinimize:o,enableSharedModalDimensions:s=!1}){const r=useIsPro(),[n,l]=useState(!1),[i,d]=useState("stores"),[c,u]=useState(null),[h,m]=useState(!1),[g,y]=useState(new Set),{filteredChanges:x,filter:S,setFilter:C,stats:b,stores:p,clearChanges:f,isEnabled:j,toggleCapture:_}=useZustandStateChanges(),T=useMemo(()=>0===g.size?x:x.filter(e=>!Array.from(g).some(t=>e.storeName.toLowerCase().includes(t.toLowerCase()))),[x,g]),k=useMemo(()=>0===g.size?p:p.filter(e=>!Array.from(g).some(t=>e.name.toLowerCase().includes(t.toLowerCase()))),[p,g]),B=useMemo(()=>r?T:T.slice(0,25),[T,r]),w=useMemo(()=>r?0:Math.max(0,T.length-25),[T.length,r]),v=w>0,M=useMemo(()=>c?B.filter(e=>e.storeName===c):B,[B,c]),[P,O]=useState(null),z=useMemo(()=>{if(null===P)return null;const e=M.findIndex(e=>e.id===P);return e>=0?e:null},[P,M]),I=null!==z?M[z]:null,[E,H]=useState(""),[A,F]=useState(!1),Z=useRef(null),N=useRef(null),V=useCallback(e=>{},[]),R=e=>{H(e),"events"===i&&C(t=>({...t,searchText:e}))},L=useCallback(e=>{O(e.id)},[]),D=useCallback(()=>{l(!0)},[]),K=useCallback(()=>{O(null)},[]),W=useCallback(e=>{const t=M[e];t&&O(t.id)},[M]),$=useCallback(e=>{u(e),O(null)},[]),G=useCallback(()=>{u(null),O(null)},[]),U=useCallback(()=>k.reduce((e,t)=>{try{const a=t.api.getState();if(a&&"object"==typeof a){const o={};for(const[e,t]of Object.entries(a))"function"!=typeof t&&(o[e]=truncatePayload(t));e[t.name]=o}else e[t.name]=truncatePayload(a)}catch{e[t.name]=null}return e},{}),[k]),J=useCallback(()=>T.map(e=>({id:e.id,storeName:e.storeName,timestamp:e.timestamp,prevState:truncatePayload(e.prevState),nextState:truncatePayload(e.nextState),hasStateChange:e.hasStateChange,changedKeys:e.changedKeys})),[T]),Q=useCallback(()=>M.map(e=>({id:e.id,storeName:e.storeName,timestamp:e.timestamp,prevState:truncatePayload(e.prevState),nextState:truncatePayload(e.nextState),hasStateChange:e.hasStateChange,changedKeys:e.changedKeys})),[M]),q=useCallback(e=>{y(t=>{const a=new Set(t);return a.has(e)?a.delete(e):a.add(e),a})},[]),Y=useCallback(e=>{y(t=>new Set([...t,e]))},[]),ee=useCallback(e=>{d(e),u(null),O(null),m(!1),E&&(H(""),C(e=>({...e,searchText:""}))),F(!1)},[E,C]),te=useCallback(e=>e.id,[]),ae=useCallback(({item:e})=>_jsx(ZustandStateChangeItem,{change:e,onPress:L}),[L]),oe=g.size>0,se=s?devToolsStorageKeys.modal.root():"buoy-zustand-modal";if(!e)return null;const re=I&&null!==z?_jsx(ZustandStateDetailFooter,{change:I,changes:M,selectedIndex:z,onIndexChange:W}):null,ne=I&&M.length>1?68:0;return _jsxs(TickProvider,{children:[_jsx(JsModal,{visible:e,onClose:t,onMinimize:o,persistenceKey:se,header:{showToggleButton:!0,customContent:_jsxs(ModalHeader,h?{children:[_jsx(ModalHeader.Navigation,{onBack:()=>m(!1)}),_jsx(ModalHeader.Content,{title:"Filters",centered:!0})]}:I?{children:[_jsx(ModalHeader.Navigation,{onBack:K}),_jsx(ModalHeader.Content,{title:`${I.storeName}/setState`,centered:!0})]}:c?{children:[_jsx(ModalHeader.Navigation,{onBack:G}),_jsx(ModalHeader.Content,{title:`${c} History`,centered:!0}),_jsx(ModalHeader.Actions,{children:_jsx(ToolbarCopyButton,{value:Q,disabled:0===M.length,buttonStyle:styles.headerActionButton})})]}:{children:[a&&_jsx(ModalHeader.Navigation,{onBack:a}),_jsx(ModalHeader.Content,{title:"",children:A?_jsxs(View,{style:styles.headerSearchContainer,children:[_jsx(Search,{size:14,color:macOSColors.text.secondary}),_jsx(TextInput,{ref:Z,style:styles.headerSearchInput,placeholder:"stores"===i?"Search stores...":"Search events...",placeholderTextColor:macOSColors.text.muted,value:E,onChangeText:R,onSubmitEditing:()=>F(!1),onBlur:()=>F(!1),autoCapitalize:"none",autoCorrect:!1,returnKeyType:"search"}),E.length>0&&_jsx(TouchableOpacity,{onPress:()=>{R(""),F(!1)},style:styles.headerSearchClear,children:_jsx(X,{size:14,color:macOSColors.text.secondary})})]}):_jsx(TabSelector,{tabs:[{key:"stores",label:"Stores"+(k.length>0?` (${k.length})`:"")},{key:"events",label:"Events"+(T.length>0?` (${T.length})`:"")}],activeTab:i,onTabChange:ee})}),_jsxs(ModalHeader.Actions,{children:[_jsx(TouchableOpacity,{onPress:()=>F(!0),style:styles.headerActionButton,children:_jsx(Search,{size:14,color:macOSColors.text.secondary})}),_jsx(TouchableOpacity,{onPress:()=>m(!0),style:[styles.headerActionButton,oe&&styles.headerActionButtonActive],children:_jsx(Filter,{size:14,color:oe?macOSColors.semantic.debug:macOSColors.text.secondary})}),"stores"===i&&_jsx(ToolbarCopyButton,{value:U,disabled:0===k.length,buttonStyle:styles.headerActionButton}),"events"===i&&_jsxs(_Fragment,{children:[_jsx(ToolbarCopyButton,{value:J,disabled:0===T.length,buttonStyle:styles.headerActionButton}),_jsx(PowerToggleButton,{isEnabled:j,onToggle:_,accessibilityLabel:"Toggle Zustand state capture"}),_jsx(ToolbarClearButton,{onPress:f,disabled:0===T.length,buttonStyle:styles.headerActionButton})]})]})]})},onModeChange:V,enablePersistence:!0,initialMode:"bottomSheet",enableGlitchEffects:!0,styles:{},footer:re,footerHeight:ne,children:_jsx(View,{style:styles.container,children:h?_jsx(ZustandEventFilterView,{ignoredPatterns:g,onTogglePattern:q,onAddPattern:Y,stores:p}):I&&null!==z?_jsx(ZustandStateDetailContent,{change:I,changes:M,selectedIndex:z,onIndexChange:W,disableInternalFooter:!0}):c?M.length>0?_jsx(FlatList,{data:M,renderItem:ae,keyExtractor:te,contentContainerStyle:styles.listContent,showsVerticalScrollIndicator:!0,removeClippedSubviews:!0,initialNumToRender:15,maxToRenderPerBatch:10,windowSize:10,scrollEnabled:!1}):_jsxs(View,{style:styles.emptyState,children:[_jsx(Box,{size:32,color:macOSColors.text.muted}),_jsx(Text,{style:styles.emptyTitle,children:"No history yet"}),_jsxs(Text,{style:styles.emptyText,children:["State changes for ",c," will appear here."]})]}):"stores"===i?_jsx(ZustandStoreBrowser,{stores:k,searchQuery:E,onViewHistory:$}):_jsxs(_Fragment,{children:[!j&&_jsxs(View,{style:styles.disabledBanner,children:[_jsx(Power,{size:14,color:macOSColors.semantic.warning}),_jsx(Text,{style:styles.disabledText,children:"State capture is disabled"})]}),v&&_jsxs(TouchableOpacity,{style:styles.lockedBanner,onPress:D,activeOpacity:.8,children:[_jsx(Lock,{size:14,color:buoyColors.primary}),_jsxs(Text,{style:styles.lockedText,children:[w," older"," ",1===w?"change":"changes"," locked"]}),_jsx(View,{style:styles.upgradeBadge,children:_jsx(Text,{style:styles.upgradeBadgeText,children:"UPGRADE"})})]}),B.length>0?_jsx(FlatList,{ref:N,data:B,renderItem:ae,keyExtractor:te,contentContainerStyle:styles.listContent,showsVerticalScrollIndicator:!0,removeClippedSubviews:!0,initialNumToRender:15,maxToRenderPerBatch:10,windowSize:10,scrollEnabled:!1}):_jsx(EventsEmptyState,{isEnabled:j})]})})}),_jsx(ProUpgradeModal,{visible:n,onClose:()=>l(!1),featureName:"Full State History"})]})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:macOSColors.background.base},headerSearchContainer:{flexDirection:"row",alignItems:"center",backgroundColor:macOSColors.background.input,borderRadius:10,borderWidth:1,borderColor:macOSColors.border.default,paddingHorizontal:12,paddingVertical:5},headerSearchInput:{flex:1,color:macOSColors.text.primary,fontSize:13,marginLeft:6,paddingVertical:2},headerSearchClear:{marginLeft:6,padding:4},headerActionButton:{width:32,height:32,borderRadius:8,backgroundColor:macOSColors.background.hover,borderWidth:1,borderColor:macOSColors.border.default,alignItems:"center",justifyContent:"center"},headerActionButtonDisabled:{opacity:.55},headerActionButtonActive:{backgroundColor:macOSColors.semantic.infoBackground},disabledBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:macOSColors.semantic.warningBackground,borderRadius:8,borderWidth:1,borderColor:macOSColors.semantic.warning+"20"},disabledText:{color:macOSColors.semantic.warning,fontSize:11,flex:1},listContent:{paddingTop:8,paddingBottom:20},emptyState:{alignItems:"center",paddingVertical:40},emptyTitle:{color:macOSColors.text.primary,fontSize:14,fontWeight:"600",marginTop:12,marginBottom:6},emptyText:{color:macOSColors.text.muted,fontSize:12,textAlign:"center",lineHeight:18},lockedBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:buoyColors.primary+"15",borderRadius:8,borderWidth:1,borderColor:buoyColors.primary+"33"},lockedText:{color:buoyColors.primary,fontSize:11,fontWeight:"500",flex:1},upgradeBadge:{backgroundColor:buoyColors.primary,paddingHorizontal:8,paddingVertical:3,borderRadius:4},upgradeBadgeText:{color:"#fff",fontSize:9,fontWeight:"700",letterSpacing:.5}});