@buoy-gg/redux 7.0.41 → 7.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +11 -106
  2. package/lib/commonjs/index.js +1 -256
  3. package/lib/commonjs/preset.js +1 -90
  4. package/lib/commonjs/redux/components/ReduxActionButton.js +1 -131
  5. package/lib/commonjs/redux/components/ReduxActionDetailContent.js +1 -437
  6. package/lib/commonjs/redux/components/ReduxActionDetailView.js +1 -401
  7. package/lib/commonjs/redux/components/ReduxActionInfoView.js +1 -836
  8. package/lib/commonjs/redux/components/ReduxActionItem.js +1 -337
  9. package/lib/commonjs/redux/components/ReduxAutoInstrument.js +1 -20
  10. package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +1 -50
  11. package/lib/commonjs/redux/components/ReduxIcon.js +1 -18
  12. package/lib/commonjs/redux/components/ReduxModal.js +1 -703
  13. package/lib/commonjs/redux/hooks/reduxPeer.js +1 -17
  14. package/lib/commonjs/redux/hooks/reduxPeer.web.js +1 -10
  15. package/lib/commonjs/redux/hooks/useAutoInstrumentRedux.js +1 -177
  16. package/lib/commonjs/redux/hooks/useReduxActions.js +1 -58
  17. package/lib/commonjs/redux/sync/reduxSyncAdapter.js +1 -191
  18. package/lib/commonjs/redux/types/index.js +1 -1
  19. package/lib/commonjs/redux/utils/activeStoreRegistry.js +1 -104
  20. package/lib/commonjs/redux/utils/autoInstrument.js +1 -231
  21. package/lib/commonjs/redux/utils/buoyReduxMiddleware.js +1 -202
  22. package/lib/commonjs/redux/utils/createReduxHistoryAdapter.js +1 -146
  23. package/lib/commonjs/redux/utils/devtoolsHook.js +1 -152
  24. package/lib/commonjs/redux/utils/reduxActionStore.js +1 -473
  25. package/lib/commonjs/redux/utils/snapshotProvider.js +1 -158
  26. package/lib/commonjs/redux/utils/testIDs.js +1 -75
  27. package/lib/module/index.js +1 -125
  28. package/lib/module/preset.js +1 -87
  29. package/lib/module/redux/components/ReduxActionButton.js +1 -128
  30. package/lib/module/redux/components/ReduxActionDetailContent.js +1 -433
  31. package/lib/module/redux/components/ReduxActionDetailView.js +1 -397
  32. package/lib/module/redux/components/ReduxActionInfoView.js +1 -831
  33. package/lib/module/redux/components/ReduxActionItem.js +1 -333
  34. package/lib/module/redux/components/ReduxAutoInstrument.js +1 -17
  35. package/lib/module/redux/components/ReduxDetailViewToggle.js +1 -45
  36. package/lib/module/redux/components/ReduxIcon.js +1 -8
  37. package/lib/module/redux/components/ReduxModal.js +1 -698
  38. package/lib/module/redux/hooks/reduxPeer.js +1 -13
  39. package/lib/module/redux/hooks/reduxPeer.web.js +1 -6
  40. package/lib/module/redux/hooks/useAutoInstrumentRedux.js +1 -173
  41. package/lib/module/redux/hooks/useReduxActions.js +1 -54
  42. package/lib/module/redux/sync/reduxSyncAdapter.js +1 -188
  43. package/lib/module/redux/types/index.js +1 -1
  44. package/lib/module/redux/utils/activeStoreRegistry.js +1 -92
  45. package/lib/module/redux/utils/autoInstrument.js +1 -201
  46. package/lib/module/redux/utils/buoyReduxMiddleware.js +1 -188
  47. package/lib/module/redux/utils/createReduxHistoryAdapter.js +1 -142
  48. package/lib/module/redux/utils/devtoolsHook.js +1 -145
  49. package/lib/module/redux/utils/reduxActionStore.js +1 -465
  50. package/lib/module/redux/utils/snapshotProvider.js +1 -157
  51. package/lib/module/redux/utils/testIDs.js +1 -68
  52. package/lib/web/index.mjs +1 -2
  53. package/package.json +5 -8
  54. package/lib/typescript/index.d.ts.map +0 -1
  55. package/lib/typescript/preset.d.ts.map +0 -1
  56. package/lib/typescript/redux/components/ReduxActionButton.d.ts.map +0 -1
  57. package/lib/typescript/redux/components/ReduxActionDetailContent.d.ts.map +0 -1
  58. package/lib/typescript/redux/components/ReduxActionDetailView.d.ts.map +0 -1
  59. package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +0 -1
  60. package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +0 -1
  61. package/lib/typescript/redux/components/ReduxAutoInstrument.d.ts.map +0 -1
  62. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +0 -1
  63. package/lib/typescript/redux/components/ReduxIcon.d.ts.map +0 -1
  64. package/lib/typescript/redux/components/ReduxModal.d.ts.map +0 -1
  65. package/lib/typescript/redux/hooks/reduxPeer.d.ts.map +0 -1
  66. package/lib/typescript/redux/hooks/reduxPeer.web.d.ts.map +0 -1
  67. package/lib/typescript/redux/hooks/useAutoInstrumentRedux.d.ts.map +0 -1
  68. package/lib/typescript/redux/hooks/useReduxActions.d.ts.map +0 -1
  69. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +0 -1
  70. package/lib/typescript/redux/types/index.d.ts.map +0 -1
  71. package/lib/typescript/redux/utils/activeStoreRegistry.d.ts.map +0 -1
  72. package/lib/typescript/redux/utils/autoInstrument.d.ts.map +0 -1
  73. package/lib/typescript/redux/utils/buoyReduxMiddleware.d.ts.map +0 -1
  74. package/lib/typescript/redux/utils/createReduxHistoryAdapter.d.ts.map +0 -1
  75. package/lib/typescript/redux/utils/devtoolsHook.d.ts.map +0 -1
  76. package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +0 -1
  77. package/lib/typescript/redux/utils/snapshotProvider.d.ts.map +0 -1
  78. package/lib/typescript/redux/utils/testIDs.d.ts.map +0 -1
@@ -1,703 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.FREE_TIER_ACTION_LIMIT = void 0;
7
- exports.ReduxModal = ReduxModal;
8
- exports.ReduxToolView = ReduxToolView;
9
- var _react = require("react");
10
- var _reactNative = require("react-native");
11
- var _sharedUi = require("@buoy-gg/shared-ui");
12
- var _license = require("@buoy-gg/license");
13
- var _useReduxActions = require("../hooks/useReduxActions");
14
- var _reduxActionStore = require("../utils/reduxActionStore");
15
- var _useAutoInstrumentRedux = require("../hooks/useAutoInstrumentRedux");
16
- var _ReduxActionItem = require("./ReduxActionItem");
17
- var _ReduxActionDetailContent = require("./ReduxActionDetailContent");
18
- var _testIDs = require("../utils/testIDs");
19
- var _jsxRuntime = require("react/jsx-runtime");
20
- /**
21
- * Main Redux DevTools modal
22
- *
23
- * This component auto-instruments the Redux store when it mounts,
24
- * providing zero-config action capture for the DevTools.
25
- */
26
-
27
- /**
28
- * @deprecated Read the live limit with `useLimit("redux.actions")` — this
29
- * constant only ever reports the FREE tier's value and is blind to the
30
- * anonymous tier. Kept because it is part of the package's public exports.
31
- */
32
- const FREE_TIER_ACTION_LIMIT = exports.FREE_TIER_ACTION_LIMIT = _sharedUi.TIER_LIMITS["redux.actions"].free;
33
- /**
34
- * Why this takes the query as well as the enabled flag: "nothing was captured"
35
- * and "nothing matched what you typed" are different problems, and an empty
36
- * list under a live search reads as a dead tool. Naming the query back is what
37
- * makes the difference legible without clearing the field to find out.
38
- */
39
- function EmptyState({
40
- isEnabled,
41
- query,
42
- totalActions
43
- }) {
44
- const content = (0, _sharedUi.resolveListEmptyState)({
45
- subject: "actions",
46
- search: query,
47
- total: totalActions,
48
- capturing: isEnabled,
49
- waiting: {
50
- otto: "waiting",
51
- title: "No Redux actions",
52
- message: "Actions will appear here as they are dispatched"
53
- },
54
- paused: {
55
- title: "No Redux actions",
56
- message: "Enable capture to start recording actions"
57
- }
58
- });
59
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolEmptyState, {
60
- ...content,
61
- testID: `${_testIDs.REDUX_TEST_ID}-empty`
62
- });
63
- }
64
- function ReduxModal({
65
- visible,
66
- onClose,
67
- onBack,
68
- onMinimize,
69
- enableSharedModalDimensions = false,
70
- inline = false,
71
- onActionSelect,
72
- initialSelectedActionId,
73
- initialDetailView
74
- }) {
75
- const isPro = (0, _license.useIsPro)();
76
- const actionLimit = (0, _sharedUi.useLimit)("redux.actions");
77
- const [showProModal, setShowProModal] = (0, _react.useState)(false);
78
-
79
- // Auto-instrument the Redux store (zero-config)
80
- const {
81
- isInstrumented,
82
- isLoading: isInstrumentLoading,
83
- error: instrumentError,
84
- isReactReduxAvailable,
85
- captureMechanism
86
- } = (0, _useAutoInstrumentRedux.useAutoInstrumentRedux)();
87
- const {
88
- filteredActions,
89
- filter,
90
- setFilter,
91
- stats,
92
- clearActions,
93
- isEnabled,
94
- toggleCapture
95
- } = (0, _useReduxActions.useReduxActions)();
96
-
97
- // Pro is Infinity here, so slice() returns everything and the locked count
98
- // floors at 0 — no tier branching needed at the call site.
99
- const visibleActions = (0, _react.useMemo)(() => filteredActions.slice(0, actionLimit), [filteredActions, actionLimit]);
100
- const lockedActionCount = (0, _react.useMemo)(() => Math.max(0, filteredActions.length - actionLimit), [filteredActions.length, actionLimit]);
101
- const hasLockedActions = lockedActionCount > 0;
102
-
103
- // Selection state, held as an action ID rather than as a list position.
104
- //
105
- // This list grows at the HEAD: every dispatch prepends a row and pushes
106
- // everything below it down one. An index therefore names a different action
107
- // a moment after it is taken — so a detail page opened while the app was
108
- // still dispatching silently became a detail page for some OTHER action,
109
- // with no transition and nothing to say it had happened. A devtool that
110
- // quietly swaps what it is showing you is worse than one that shows nothing.
111
- //
112
- // The index is still what the stepper and the diff's "previous action" are
113
- // expressed in — both are genuinely about position — so it is derived here
114
- // and re-derived whenever the list moves.
115
- const [selectedActionId, setSelectedActionId] = (0, _react.useState)(null);
116
- const selectedActionIndex = (0, _react.useMemo)(() => {
117
- if (selectedActionId === null) return null;
118
- const index = visibleActions.findIndex(a => a.id === selectedActionId);
119
- // Below zero means the action is gone — cleared, or aged out past the tier
120
- // cap. Falling back to the list is the honest outcome; the alternative is
121
- // showing whatever moved into its place.
122
- return index >= 0 ? index : null;
123
- }, [selectedActionId, visibleActions]);
124
- const selectedAction = selectedActionIndex !== null ? visibleActions[selectedActionIndex] : null;
125
-
126
- // Notify the host on selection (keyed by action id so re-renders with the
127
- // same selection don't re-fire; remote mirrors fetch state trees here).
128
- const lastNotifiedSelectionRef = (0, _react.useRef)(null);
129
- (0, _react.useEffect)(() => {
130
- if (!onActionSelect) return;
131
- const id = selectedAction?.id ?? null;
132
- if (id === null || id === lastNotifiedSelectionRef.current) return;
133
- lastNotifiedSelectionRef.current = id;
134
- onActionSelect(selectedAction);
135
- }, [selectedAction, onActionSelect]);
136
-
137
- // Deep link into an action's detail (see initialSelectedActionId). Applied
138
- // at most once per id, so the list re-rendering — or the user pressing Back
139
- // — never re-opens it.
140
- const appliedInitialSelectionRef = (0, _react.useRef)(null);
141
- (0, _react.useEffect)(() => {
142
- if (!initialSelectedActionId) return;
143
- if (appliedInitialSelectionRef.current === initialSelectedActionId) return;
144
- const index = visibleActions.findIndex(a => a.id === initialSelectedActionId);
145
- if (index < 0) return;
146
- appliedInitialSelectionRef.current = initialSelectedActionId;
147
- setSelectedActionId(initialSelectedActionId);
148
- }, [initialSelectedActionId, visibleActions]);
149
- const [searchText, setSearchText] = (0, _react.useState)("");
150
- const [isSearchActive, setIsSearchActive] = (0, _react.useState)(false);
151
- const searchInputRef = (0, _react.useRef)(null);
152
- const flatListRef = (0, _react.useRef)(null);
153
- const handleModeChange = (0, _react.useCallback)(_mode => {
154
- // Mode changes handled by JsModal
155
- }, []);
156
- const handleSearch = text => {
157
- setSearchText(text);
158
- setFilter(prev => ({
159
- ...prev,
160
- searchText: text
161
- }));
162
- };
163
-
164
- /** Drop the query AND put the header back — the ✕ means "stop filtering". */
165
- const closeSearch = (0, _react.useCallback)(() => {
166
- setSearchText("");
167
- setFilter(prev => ({
168
- ...prev,
169
- searchText: ""
170
- }));
171
- setIsSearchActive(false);
172
- }, [setFilter]);
173
- const handleActionPress = (0, _react.useCallback)(action => {
174
- setSelectedActionId(action.id);
175
- }, []);
176
- const handleUpgradePress = (0, _react.useCallback)(() => {
177
- setShowProModal(true);
178
- }, []);
179
- const handleBack = (0, _react.useCallback)(() => {
180
- setSelectedActionId(null);
181
- }, []);
182
-
183
- // The stepper and the async timeline both navigate by POSITION; the
184
- // selection is kept by identity, so the position is resolved to an id here,
185
- // once, at the moment of the tap.
186
- const handleIndexChange = (0, _react.useCallback)(newIndex => {
187
- const next = visibleActions[newIndex];
188
- if (next) setSelectedActionId(next.id);
189
- }, [visibleActions]);
190
- const handleCopyAll = async () => {
191
- if (!isPro) {
192
- setShowProModal(true);
193
- return;
194
- }
195
- const actionsData = filteredActions.map(action => ({
196
- id: action.id,
197
- type: action.type,
198
- ...(action.payload !== undefined && {
199
- payload: action.payload
200
- }),
201
- timestamp: action.timestamp,
202
- prevState: action.prevState,
203
- nextState: action.nextState,
204
- hasStateChange: action.hasStateChange
205
- }));
206
- await (0, _sharedUi.copyToClipboard)(actionsData);
207
- };
208
-
209
- // FlatList optimization
210
- const keyExtractor = (0, _react.useCallback)(item => item.id, []);
211
-
212
- // An action row's handle carries an ordinal among rows of the SAME type, and
213
- // an ordinal is a fact about the list rather than about the row — see
214
- // ../utils/testIDs.ts for why a type alone cannot address a redux row.
215
- const rowTestIDs = (0, _react.useMemo)(() => (0, _testIDs.buildActionRowTestIDs)(visibleActions), [visibleActions]);
216
-
217
- // How many captured actions share each requestId, i.e. how far along each
218
- // async operation is.
219
- //
220
- // Computed HERE rather than in the row, and passed down, because a row's
221
- // async badge is a fact about OTHER rows. `pending` is captured and drawn
222
- // while it is still alone in the world; `fulfilled` arrives a moment later
223
- // and makes the earlier row's badge wrong. `ReduxActionItem` is memoized on
224
- // its props and the action object it holds never changes identity, so
225
- // nothing about that second arrival reached the first row — the pending half
226
- // of every settled thunk permanently showed no flow badge, which is exactly
227
- // backwards from what the badge is for.
228
- //
229
- // One pass over the store instead of a per-row scan: the previous shape was
230
- // O(rows x actions) at 200 actions, on a list that re-renders on every
231
- // dispatch.
232
- const linkedCounts = (0, _react.useMemo)(() => {
233
- const counts = new Map();
234
- for (const action of _reduxActionStore.reduxActionStore.getActions()) {
235
- const requestId = action.meta?.requestId;
236
- if (!requestId) continue;
237
- counts.set(requestId, (counts.get(requestId) ?? 0) + 1);
238
- }
239
- return counts;
240
- // `filteredActions` takes a new identity whenever the store notifies, which
241
- // is the only moment these counts can change.
242
- }, [filteredActions]);
243
- const renderItem = (0, _react.useCallback)(({
244
- item
245
- }) => {
246
- const requestId = item.meta?.requestId;
247
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ReduxActionItem.ReduxActionItem, {
248
- action: item,
249
- onPress: handleActionPress,
250
- linkedCount: requestId ? linkedCounts.get(requestId) ?? 0 : 0,
251
- testID: rowTestIDs.get(item.id)
252
- });
253
- }, [handleActionPress, rowTestIDs, linkedCounts]);
254
- const renderHeaderContent = () => {
255
- // Detail view header - show action type
256
- if (selectedAction) {
257
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
258
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
259
- onBack: handleBack
260
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
261
- title: selectedAction.type,
262
- centered: true,
263
- testID: `${_testIDs.REDUX_TEST_ID}-detail-title`
264
- })]
265
- });
266
- }
267
-
268
- // Main list view header
269
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader, {
270
- children: [onBack && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation, {
271
- onBack: onBack
272
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Content, {
273
- title: "",
274
- children: isSearchActive ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Search, {
275
- value: searchText,
276
- onChangeText: handleSearch,
277
- onCancel: closeSearch,
278
- placeholder: "Search action types...",
279
- accessibilityLabel: "Search action types",
280
- iconSize: 14,
281
- testID: `${_testIDs.REDUX_TEST_ID}-search-field`,
282
- containerTestID: `${_testIDs.REDUX_TEST_ID}-search`,
283
- clearTestID: `${_testIDs.REDUX_TEST_ID}-search-clear`
284
- // Stay open while the query still filters the list.
285
- //
286
- // Collapsing it on blur hid the reason rows were missing: the
287
- // header went back to its chips — which count the WHOLE
288
- // capture, not the filtered list — while the list stayed
289
- // narrowed, so the tool showed "4 actions" above a single row
290
- // and offered nothing to explain the gap. The only way back
291
- // was to reopen a field the user had no reason to think was
292
- // still armed.
293
- ,
294
- onBlur: () => {
295
- if (searchText.length === 0) setIsSearchActive(false);
296
- }
297
- }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
298
- style: styles.headerChipRow,
299
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
300
- style: [styles.headerChip, !filter.onlyWithChanges && styles.headerChipActive],
301
- onPress: () => setFilter(prev => ({
302
- ...prev,
303
- onlyWithChanges: false
304
- })),
305
- testID: `${_testIDs.REDUX_TEST_ID}-chip-all`,
306
- accessibilityRole: "button",
307
- accessibilityLabel: `Show all actions, ${stats.totalActions} captured`,
308
- accessibilityState: {
309
- selected: !filter.onlyWithChanges
310
- },
311
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
312
- style: styles.headerChipLabel,
313
- children: "ALL"
314
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
315
- style: [styles.headerChipValue, {
316
- color: _sharedUi.night.color.text
317
- }],
318
- testID: `${_testIDs.REDUX_TEST_ID}-chip-all-value`,
319
- children: stats.totalActions
320
- })]
321
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
322
- style: [styles.headerChip, filter.onlyWithChanges && styles.headerChipActive],
323
- onPress: () => setFilter(prev => ({
324
- ...prev,
325
- onlyWithChanges: true
326
- })),
327
- testID: `${_testIDs.REDUX_TEST_ID}-chip-changed`,
328
- accessibilityRole: "button",
329
- accessibilityLabel: `Show only actions that changed state, ${stats.actionsWithChanges} of them`,
330
- accessibilityState: {
331
- selected: !!filter.onlyWithChanges
332
- },
333
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Zap, {
334
- size: 12,
335
- color: _sharedUi.macOSColors.semantic.warning
336
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
337
- style: [styles.headerChipValue, {
338
- color: _sharedUi.macOSColors.semantic.warning
339
- }],
340
- testID: `${_testIDs.REDUX_TEST_ID}-chip-changed-value`,
341
- children: stats.actionsWithChanges
342
- })]
343
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
344
- style: styles.headerChip,
345
- testID: `${_testIDs.REDUX_TEST_ID}-chip-types`,
346
- accessibilityLabel: `${stats.uniqueActionTypes} distinct action types`,
347
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CheckCircle, {
348
- size: 12,
349
- color: _sharedUi.macOSColors.semantic.success
350
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
351
- style: [styles.headerChipValue, {
352
- color: _sharedUi.macOSColors.semantic.success
353
- }],
354
- testID: `${_testIDs.REDUX_TEST_ID}-chip-types-value`,
355
- children: stats.uniqueActionTypes
356
- })]
357
- })]
358
- })
359
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
360
- children: [!isSearchActive && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
361
- onPress: () => setIsSearchActive(true),
362
- style: [styles.headerActionButton, searchText.length > 0 && styles.headerActionButtonActive],
363
- testID: `${_testIDs.REDUX_TEST_ID}-search-open`,
364
- accessibilityRole: "button",
365
- accessibilityLabel: "Search action types",
366
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
367
- size: 14,
368
- color: searchText.length > 0 ? _sharedUi.night.color.accent : _sharedUi.night.color.textSecondary
369
- })
370
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
371
- onPress: handleCopyAll,
372
- style: [styles.headerActionButton, filteredActions.length === 0 && styles.headerActionButtonDisabled],
373
- disabled: filteredActions.length === 0,
374
- testID: `${_testIDs.REDUX_TEST_ID}-copy`,
375
- accessibilityRole: "button",
376
- accessibilityLabel: "Copy all actions",
377
- accessibilityState: {
378
- disabled: filteredActions.length === 0
379
- },
380
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Copy, {
381
- size: 14,
382
- color: filteredActions.length > 0 ? _sharedUi.night.color.textSecondary : _sharedUi.night.color.textTertiary
383
- })
384
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.PowerToggleButton, {
385
- isEnabled: isEnabled,
386
- onToggle: toggleCapture,
387
- accessibilityLabel: "Toggle Redux action capture",
388
- testID: `${_testIDs.REDUX_TEST_ID}-power`
389
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
390
- onPress: clearActions,
391
- style: [styles.headerActionButton, filteredActions.length === 0 && styles.headerActionButtonDisabled],
392
- disabled: filteredActions.length === 0,
393
- testID: `${_testIDs.REDUX_TEST_ID}-clear`,
394
- accessibilityRole: "button",
395
- accessibilityLabel: "Clear actions",
396
- accessibilityState: {
397
- disabled: filteredActions.length === 0
398
- },
399
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Trash2, {
400
- size: 14,
401
- color: filteredActions.length > 0 ? _sharedUi.night.color.textTertiary : _sharedUi.night.color.textTertiary
402
- })
403
- })]
404
- })]
405
- });
406
- };
407
- const persistenceKey = enableSharedModalDimensions ? _sharedUi.devToolsStorageKeys.modal.root() : "buoy-redux-modal";
408
- if (!visible) return null;
409
-
410
- // Footer for detail view navigation
411
- const footerNode = selectedAction && selectedActionIndex !== null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ReduxActionDetailContent.ReduxActionDetailFooter, {
412
- action: selectedAction,
413
- actions: visibleActions,
414
- selectedIndex: selectedActionIndex,
415
- onIndexChange: handleIndexChange
416
- }) : null;
417
- const footerHeight = selectedAction && visibleActions.length > 1 ? 68 : 0;
418
-
419
- // The action list owns its scrolling so FlatList virtualization works
420
- // (see ZustandModal — same pattern, same tool-bench evidence).
421
- const listOwnsScroll = selectedAction == null;
422
- const body = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
423
- style: styles.container,
424
- children: selectedAction && selectedActionIndex !== null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ReduxActionDetailContent.ReduxActionDetailContent, {
425
- action: selectedAction,
426
- actions: visibleActions,
427
- selectedIndex: selectedActionIndex,
428
- onIndexChange: handleIndexChange,
429
- disableInternalFooter: true,
430
- initialView: initialDetailView
431
- }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
432
- children: [instrumentError && !isInstrumented && !_reduxActionStore.reduxActionStore.isCaptureSuppressed() && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
433
- style: styles.errorBanner,
434
- testID: `${_testIDs.REDUX_TEST_ID}-banner-error`,
435
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
436
- size: 14,
437
- color: _sharedUi.night.color.danger
438
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
439
- style: styles.errorText,
440
- children: isReactReduxAvailable ? "Not inside a Redux Provider. Wrap your app with <Provider store={store}>." : "react-redux not installed. Run: npm install react-redux"
441
- })]
442
- }), !isEnabled && isInstrumented && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
443
- style: styles.disabledBanner,
444
- testID: `${_testIDs.REDUX_TEST_ID}-banner-disabled`,
445
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Power, {
446
- size: 14,
447
- color: _sharedUi.night.color.warning
448
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
449
- style: styles.disabledText,
450
- children: "Action capture is disabled"
451
- })]
452
- }), captureMechanism === "patch" && !_reduxActionStore.reduxActionStore.isCaptureSuppressed() && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
453
- style: styles.disabledBanner,
454
- testID: `${_testIDs.REDUX_TEST_ID}-banner-patch`,
455
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.AlertTriangle, {
456
- size: 14,
457
- color: _sharedUi.night.color.warning
458
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
459
- style: styles.disabledText,
460
- children: "Top-level capture only \u2014 thunk/RTK Query internals aren't visible. Put import '@buoy-gg/redux' first in your app entry (or add buoyReduxMiddleware) for full capture."
461
- })]
462
- }), hasLockedActions && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
463
- style: styles.lockedBanner,
464
- onPress: handleUpgradePress,
465
- activeOpacity: 0.8,
466
- testID: `${_testIDs.REDUX_TEST_ID}-locked`,
467
- accessibilityRole: "button",
468
- accessibilityLabel: `${lockedActionCount} older ${lockedActionCount === 1 ? "action" : "actions"} locked. Upgrade to see them.`,
469
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Lock, {
470
- size: 14,
471
- color: _sharedUi.night.color.accent
472
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
473
- style: styles.lockedText,
474
- testID: `${_testIDs.REDUX_TEST_ID}-locked-text`,
475
- children: [lockedActionCount, " older ", lockedActionCount === 1 ? "action" : "actions", " locked"]
476
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
477
- style: styles.upgradeBadge,
478
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
479
- style: styles.upgradeBadgeText,
480
- children: "UPGRADE"
481
- })
482
- })]
483
- }), visibleActions.length > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
484
- ref: flatListRef,
485
- testID: `${_testIDs.REDUX_TEST_ID}-list`,
486
- data: visibleActions,
487
- renderItem: renderItem,
488
- keyExtractor: keyExtractor,
489
- contentContainerStyle: styles.listContent,
490
- showsVerticalScrollIndicator: true,
491
- ..._sharedUi.EVENT_LIST_PRESET
492
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(EmptyState, {
493
- isEnabled: isEnabled,
494
- query: searchText,
495
- totalActions: stats.totalActions
496
- })]
497
- })
498
- });
499
- const upgradeModal = /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
500
- visible: showProModal,
501
- onClose: () => setShowProModal(false),
502
- featureName: "Full Action History"
503
- });
504
-
505
- // The ONLY difference between the phone and the desktop dashboard is which
506
- // shell the header, body and footer go in. Everything above this is shared.
507
- if (inline) {
508
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.TickProvider, {
509
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
510
- style: styles.inlineRoot,
511
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
512
- style: styles.inlineHeader,
513
- ..._sharedUi.windowDragProps,
514
- children: renderHeaderContent()
515
- }), body, footerNode]
516
- }), upgradeModal]
517
- });
518
- }
519
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.TickProvider, {
520
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.JsModal, {
521
- visible: visible,
522
- onClose: onClose,
523
- onMinimize: onMinimize,
524
- persistenceKey: persistenceKey,
525
- header: {
526
- showToggleButton: true,
527
- customContent: renderHeaderContent()
528
- },
529
- onModeChange: handleModeChange,
530
- enablePersistence: true,
531
- initialMode: "bottomSheet",
532
- enableGlitchEffects: true,
533
- disableScrollWrapper: listOwnsScroll,
534
- styles: {},
535
- footer: footerNode,
536
- footerHeight: footerHeight,
537
- children: body
538
- }), upgradeModal]
539
- });
540
- }
541
-
542
- /**
543
- * The same Redux tool as `ReduxModal`, rendered INLINE for hosts that place
544
- * tools in a panel rather than a modal. Exists so there is exactly one
545
- * implementation — the desktop dashboard used to ship its own copy of this
546
- * component's header, action list and detail router.
547
- */
548
- function ReduxToolView(props = {}) {
549
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(ReduxModal, {
550
- ...props,
551
- inline: true,
552
- visible: true,
553
- onClose: () => {}
554
- });
555
- }
556
- const styles = _reactNative.StyleSheet.create({
557
- inlineRoot: {
558
- flex: 1,
559
- minHeight: 0,
560
- // Transparent so the desktop workbench's hoisted sky (one field spanning
561
- // sidebar + panel) shows through — the shell paints its base beneath it.
562
- backgroundColor: "transparent"
563
- },
564
- inlineHeader: {
565
- paddingHorizontal: 12,
566
- paddingVertical: 8,
567
- borderBottomWidth: 1,
568
- borderBottomColor: _sharedUi.macOSColors.border.default,
569
- // Clear, like Bench — the host's hoisted sky shows through the header.
570
- backgroundColor: "transparent"
571
- },
572
- container: {
573
- flex: 1,
574
- // Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
575
- backgroundColor: "transparent"
576
- },
577
- headerChipRow: {
578
- flexDirection: "row",
579
- alignItems: "center",
580
- gap: 8
581
- },
582
- headerChip: {
583
- flexDirection: "row",
584
- alignItems: "center",
585
- gap: 4,
586
- backgroundColor: _sharedUi.night.color.surfaceElevated,
587
- paddingHorizontal: 10,
588
- paddingVertical: 5,
589
- borderRadius: 12,
590
- borderWidth: 1,
591
- borderColor: _sharedUi.night.color.border
592
- },
593
- headerChipActive: {
594
- // Active chip chrome takes the night accent, not info-cyan.
595
- backgroundColor: _sharedUi.night.color.accentSoft,
596
- borderColor: _sharedUi.night.color.accentBorderStrong
597
- },
598
- headerChipLabel: {
599
- fontSize: 10,
600
- fontWeight: "700",
601
- color: _sharedUi.night.color.textTertiary
602
- },
603
- headerChipValue: {
604
- fontSize: 12,
605
- fontWeight: "600",
606
- fontFamily: _sharedUi.monoFont
607
- },
608
- headerActionButton: {
609
- width: 32,
610
- height: 32,
611
- borderRadius: 8,
612
- backgroundColor: _sharedUi.night.color.surfaceElevated,
613
- borderWidth: 1,
614
- borderColor: _sharedUi.night.color.border,
615
- alignItems: "center",
616
- justifyContent: "center"
617
- },
618
- headerActionButtonDisabled: {
619
- opacity: 0.55
620
- },
621
- headerActionButtonActive: {
622
- backgroundColor: _sharedUi.night.color.accentSoft,
623
- borderColor: _sharedUi.night.color.accentBorderStrong
624
- },
625
- startButton: {
626
- backgroundColor: _sharedUi.macOSColors.semantic.successBackground,
627
- borderColor: _sharedUi.macOSColors.semantic.success + "40"
628
- },
629
- stopButton: {
630
- backgroundColor: _sharedUi.macOSColors.semantic.errorBackground,
631
- borderColor: _sharedUi.macOSColors.semantic.error + "40"
632
- },
633
- errorBanner: {
634
- flexDirection: "row",
635
- alignItems: "center",
636
- gap: 8,
637
- padding: 10,
638
- marginHorizontal: 12,
639
- marginTop: 8,
640
- backgroundColor: _sharedUi.night.color.dangerSoft,
641
- borderRadius: 8,
642
- borderWidth: 1,
643
- borderColor: _sharedUi.night.color.danger + "20"
644
- },
645
- errorText: {
646
- color: _sharedUi.night.color.danger,
647
- fontSize: 11,
648
- flex: 1
649
- },
650
- disabledBanner: {
651
- flexDirection: "row",
652
- alignItems: "center",
653
- gap: 8,
654
- padding: 10,
655
- marginHorizontal: 12,
656
- marginTop: 8,
657
- backgroundColor: _sharedUi.night.color.warningSoft,
658
- borderRadius: 8,
659
- borderWidth: 1,
660
- borderColor: _sharedUi.night.color.warning + "20"
661
- },
662
- disabledText: {
663
- color: _sharedUi.night.color.warning,
664
- fontSize: 11,
665
- flex: 1
666
- },
667
- listContent: {
668
- paddingTop: 8,
669
- paddingBottom: 20
670
- },
671
- // Locked actions banner styles
672
- lockedBanner: {
673
- flexDirection: "row",
674
- alignItems: "center",
675
- gap: 8,
676
- padding: 10,
677
- marginHorizontal: 12,
678
- marginTop: 8,
679
- backgroundColor: _sharedUi.night.color.accentSoft,
680
- borderRadius: 8,
681
- borderWidth: 1,
682
- borderColor: _sharedUi.night.color.accentBorder
683
- },
684
- lockedText: {
685
- color: _sharedUi.night.color.accent,
686
- fontSize: 11,
687
- fontWeight: "500",
688
- flex: 1
689
- },
690
- upgradeBadge: {
691
- backgroundColor: _sharedUi.night.color.accent,
692
- paddingHorizontal: 8,
693
- paddingVertical: 3,
694
- borderRadius: 4
695
- },
696
- upgradeBadgeText: {
697
- // The night accent takes dark ink.
698
- color: _sharedUi.night.color.onAccent,
699
- fontSize: 9,
700
- fontWeight: "700",
701
- letterSpacing: 0.5
702
- }
703
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.FREE_TIER_ACTION_LIMIT=void 0;exports.ReduxModal=ReduxModal;exports.ReduxToolView=ReduxToolView;var _react=require("react");var _reactNative=require("react-native");var _sharedUi=require("@buoy-gg/shared-ui");var _license=require("@buoy-gg/license");var _useReduxActions=require("../hooks/useReduxActions");var _reduxActionStore=require("../utils/reduxActionStore");var _useAutoInstrumentRedux=require("../hooks/useAutoInstrumentRedux");var _ReduxActionItem=require("./ReduxActionItem");var _ReduxActionDetailContent=require("./ReduxActionDetailContent");var _testIDs=require("../utils/testIDs");var _jsxRuntime=require("react/jsx-runtime");const FREE_TIER_ACTION_LIMIT=exports.FREE_TIER_ACTION_LIMIT=_sharedUi.TIER_LIMITS["redux.actions"].free;function EmptyState({isEnabled:s,query:b,totalActions:h}){const T=(0,_sharedUi.resolveListEmptyState)({subject:"actions",search:b,total:h,capturing:s,waiting:{otto:"waiting",title:"No Redux actions",message:"Actions will appear here as they are dispatched"},paused:{title:"No Redux actions",message:"Enable capture to start recording actions"}});return(0,_jsxRuntime.jsx)(_sharedUi.ToolEmptyState,{...T,testID:`${_testIDs.REDUX_TEST_ID}-empty`})}function ReduxModal({visible:s,onClose:b,onBack:h,onMinimize:T,enableSharedModalDimensions:U=false,inline:$=false,onActionSelect:y,initialSelectedActionId:a,initialDetailView:z}){const H=(0,_license.useIsPro)();const g=(0,_sharedUi.useLimit)("redux.actions");const[O,C]=(0,_react.useState)(false);const{isInstrumented:R,isLoading:se,error:V,isReactReduxAvailable:X,captureMechanism:W}=(0,_useAutoInstrumentRedux.useAutoInstrumentRedux)();const{filteredActions:o,filter:x,setFilter:c,stats:i,clearActions:q,isEnabled:I,toggleCapture:N}=(0,_useReduxActions.useReduxActions)();const r=(0,_react.useMemo)(()=>o.slice(0,g),[o,g]);const d=(0,_react.useMemo)(()=>Math.max(0,o.length-g),[o.length,g]);const F=d>0;const[S,p]=(0,_react.useState)(null);const l=(0,_react.useMemo)(()=>{if(S===null)return null;const e=r.findIndex(t=>t.id===S);return e>=0?e:null},[S,r]);const n=l!==null?r[l]:null;const m=(0,_react.useRef)(null);(0,_react.useEffect)(()=>{if(!y)return;const e=n?.id??null;if(e===null||e===m.current)return;m.current=e;y(n)},[n,y]);const E=(0,_react.useRef)(null);(0,_react.useEffect)(()=>{if(!a)return;if(E.current===a)return;const e=r.findIndex(t=>t.id===a);if(e<0)return;E.current=a;p(a)},[a,r]);const[u,_]=(0,_react.useState)("");const[j,f]=(0,_react.useState)(false);const ae=(0,_react.useRef)(null);const K=(0,_react.useRef)(null);const G=(0,_react.useCallback)(e=>{},[]);const J=e=>{_(e);c(t=>({...t,searchText:e}))};const Q=(0,_react.useCallback)(()=>{_("");c(e=>({...e,searchText:""}));f(false)},[c]);const A=(0,_react.useCallback)(e=>{p(e.id)},[]);const Z=(0,_react.useCallback)(()=>{C(true)},[]);const Y=(0,_react.useCallback)(()=>{p(null)},[]);const w=(0,_react.useCallback)(e=>{const t=r[e];if(t)p(t.id)},[r]);const ee=async()=>{if(!H){C(true);return}const e=o.map(t=>({id:t.id,type:t.type,...t.payload!==void 0&&{payload:t.payload},timestamp:t.timestamp,prevState:t.prevState,nextState:t.nextState,hasStateChange:t.hasStateChange}));await(0,_sharedUi.copyToClipboard)(e)};const te=(0,_react.useCallback)(e=>e.id,[]);const k=(0,_react.useMemo)(()=>(0,_testIDs.buildActionRowTestIDs)(r),[r]);const v=(0,_react.useMemo)(()=>{const e=new Map;for(const t of _reduxActionStore.reduxActionStore.getActions()){const D=t.meta?.requestId;if(!D)continue;e.set(D,(e.get(D)??0)+1)}return e},[o]);const oe=(0,_react.useCallback)(({item:e})=>{const t=e.meta?.requestId;return(0,_jsxRuntime.jsx)(_ReduxActionItem.ReduxActionItem,{action:e,onPress:A,linkedCount:t?v.get(t)??0:0,testID:k.get(e.id)})},[A,k,v]);const B=()=>{if(n){return(0,_jsxRuntime.jsxs)(_sharedUi.ModalHeader,{children:[(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation,{onBack:Y}),(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Content,{title:n.type,centered:true,testID:`${_testIDs.REDUX_TEST_ID}-detail-title`})]})}return(0,_jsxRuntime.jsxs)(_sharedUi.ModalHeader,{children:[h&&(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Navigation,{onBack:h}),(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Content,{title:"",children:j?(0,_jsxRuntime.jsx)(_sharedUi.ModalHeader.Search,{value:u,onChangeText:J,onCancel:Q,placeholder:"Search action types...",accessibilityLabel:"Search action types",iconSize:14,testID:`${_testIDs.REDUX_TEST_ID}-search-field`,containerTestID:`${_testIDs.REDUX_TEST_ID}-search`,clearTestID:`${_testIDs.REDUX_TEST_ID}-search-clear`,onBlur:()=>{if(u.length===0)f(false)}}):(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.headerChipRow,children:[(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.headerChip,!x.onlyWithChanges&&styles.headerChipActive],onPress:()=>c(e=>({...e,onlyWithChanges:false})),testID:`${_testIDs.REDUX_TEST_ID}-chip-all`,accessibilityRole:"button",accessibilityLabel:`Show all actions, ${i.totalActions} captured`,accessibilityState:{selected:!x.onlyWithChanges},children:[(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.headerChipLabel,children:"ALL"}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.headerChipValue,{color:_sharedUi.night.color.text}],testID:`${_testIDs.REDUX_TEST_ID}-chip-all-value`,children:i.totalActions})]}),(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:[styles.headerChip,x.onlyWithChanges&&styles.headerChipActive],onPress:()=>c(e=>({...e,onlyWithChanges:true})),testID:`${_testIDs.REDUX_TEST_ID}-chip-changed`,accessibilityRole:"button",accessibilityLabel:`Show only actions that changed state, ${i.actionsWithChanges} of them`,accessibilityState:{selected:!!x.onlyWithChanges},children:[(0,_jsxRuntime.jsx)(_sharedUi.Zap,{size:12,color:_sharedUi.macOSColors.semantic.warning}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.headerChipValue,{color:_sharedUi.macOSColors.semantic.warning}],testID:`${_testIDs.REDUX_TEST_ID}-chip-changed-value`,children:i.actionsWithChanges})]}),(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.headerChip,testID:`${_testIDs.REDUX_TEST_ID}-chip-types`,accessibilityLabel:`${i.uniqueActionTypes} distinct action types`,children:[(0,_jsxRuntime.jsx)(_sharedUi.CheckCircle,{size:12,color:_sharedUi.macOSColors.semantic.success}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:[styles.headerChipValue,{color:_sharedUi.macOSColors.semantic.success}],testID:`${_testIDs.REDUX_TEST_ID}-chip-types-value`,children:i.uniqueActionTypes})]})]})}),(0,_jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions,{children:[!j&&(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:()=>f(true),style:[styles.headerActionButton,u.length>0&&styles.headerActionButtonActive],testID:`${_testIDs.REDUX_TEST_ID}-search-open`,accessibilityRole:"button",accessibilityLabel:"Search action types",children:(0,_jsxRuntime.jsx)(_sharedUi.Search,{size:14,color:u.length>0?_sharedUi.night.color.accent:_sharedUi.night.color.textSecondary})}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:ee,style:[styles.headerActionButton,o.length===0&&styles.headerActionButtonDisabled],disabled:o.length===0,testID:`${_testIDs.REDUX_TEST_ID}-copy`,accessibilityRole:"button",accessibilityLabel:"Copy all actions",accessibilityState:{disabled:o.length===0},children:(0,_jsxRuntime.jsx)(_sharedUi.Copy,{size:14,color:o.length>0?_sharedUi.night.color.textSecondary:_sharedUi.night.color.textTertiary})}),(0,_jsxRuntime.jsx)(_sharedUi.PowerToggleButton,{isEnabled:I,onToggle:N,accessibilityLabel:"Toggle Redux action capture",testID:`${_testIDs.REDUX_TEST_ID}-power`}),(0,_jsxRuntime.jsx)(_reactNative.TouchableOpacity,{onPress:q,style:[styles.headerActionButton,o.length===0&&styles.headerActionButtonDisabled],disabled:o.length===0,testID:`${_testIDs.REDUX_TEST_ID}-clear`,accessibilityRole:"button",accessibilityLabel:"Clear actions",accessibilityState:{disabled:o.length===0},children:(0,_jsxRuntime.jsx)(_sharedUi.Trash2,{size:14,color:o.length>0?_sharedUi.night.color.textTertiary:_sharedUi.night.color.textTertiary})})]})]})};const re=U?_sharedUi.devToolsStorageKeys.modal.root():"buoy-redux-modal";if(!s)return null;const M=n&&l!==null?(0,_jsxRuntime.jsx)(_ReduxActionDetailContent.ReduxActionDetailFooter,{action:n,actions:r,selectedIndex:l,onIndexChange:w}):null;const ne=n&&r.length>1?68:0;const ie=n==null;const L=(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.container,children:n&&l!==null?(0,_jsxRuntime.jsx)(_ReduxActionDetailContent.ReduxActionDetailContent,{action:n,actions:r,selectedIndex:l,onIndexChange:w,disableInternalFooter:true,initialView:z}):(0,_jsxRuntime.jsxs)(_jsxRuntime.Fragment,{children:[V&&!R&&!_reduxActionStore.reduxActionStore.isCaptureSuppressed()&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.errorBanner,testID:`${_testIDs.REDUX_TEST_ID}-banner-error`,children:[(0,_jsxRuntime.jsx)(_sharedUi.AlertTriangle,{size:14,color:_sharedUi.night.color.danger}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.errorText,children:X?"Not inside a Redux Provider. Wrap your app with <Provider store={store}>.":"react-redux not installed. Run: npm install react-redux"})]}),!I&&R&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.disabledBanner,testID:`${_testIDs.REDUX_TEST_ID}-banner-disabled`,children:[(0,_jsxRuntime.jsx)(_sharedUi.Power,{size:14,color:_sharedUi.night.color.warning}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.disabledText,children:"Action capture is disabled"})]}),W==="patch"&&!_reduxActionStore.reduxActionStore.isCaptureSuppressed()&&(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.disabledBanner,testID:`${_testIDs.REDUX_TEST_ID}-banner-patch`,children:[(0,_jsxRuntime.jsx)(_sharedUi.AlertTriangle,{size:14,color:_sharedUi.night.color.warning}),(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.disabledText,children:"Top-level capture only \u2014 thunk/RTK Query internals aren't visible. Put import '@buoy-gg/redux' first in your app entry (or add buoyReduxMiddleware) for full capture."})]}),F&&(0,_jsxRuntime.jsxs)(_reactNative.TouchableOpacity,{style:styles.lockedBanner,onPress:Z,activeOpacity:.8,testID:`${_testIDs.REDUX_TEST_ID}-locked`,accessibilityRole:"button",accessibilityLabel:`${d} older ${d===1?"action":"actions"} locked. Upgrade to see them.`,children:[(0,_jsxRuntime.jsx)(_sharedUi.Lock,{size:14,color:_sharedUi.night.color.accent}),(0,_jsxRuntime.jsxs)(_reactNative.Text,{style:styles.lockedText,testID:`${_testIDs.REDUX_TEST_ID}-locked-text`,children:[d," older ",d===1?"action":"actions"," locked"]}),(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.upgradeBadge,children:(0,_jsxRuntime.jsx)(_reactNative.Text,{style:styles.upgradeBadgeText,children:"UPGRADE"})})]}),r.length>0?(0,_jsxRuntime.jsx)(_reactNative.FlatList,{ref:K,testID:`${_testIDs.REDUX_TEST_ID}-list`,data:r,renderItem:oe,keyExtractor:te,contentContainerStyle:styles.listContent,showsVerticalScrollIndicator:true,..._sharedUi.EVENT_LIST_PRESET}):(0,_jsxRuntime.jsx)(EmptyState,{isEnabled:I,query:u,totalActions:i.totalActions})]})});const P=(0,_jsxRuntime.jsx)(_sharedUi.ProUpgradeModal,{visible:O,onClose:()=>C(false),featureName:"Full Action History"});if($){return(0,_jsxRuntime.jsxs)(_sharedUi.TickProvider,{children:[(0,_jsxRuntime.jsxs)(_reactNative.View,{style:styles.inlineRoot,children:[(0,_jsxRuntime.jsx)(_reactNative.View,{style:styles.inlineHeader,..._sharedUi.windowDragProps,children:B()}),L,M]}),P]})}return(0,_jsxRuntime.jsxs)(_sharedUi.TickProvider,{children:[(0,_jsxRuntime.jsx)(_sharedUi.JsModal,{visible:s,onClose:b,onMinimize:T,persistenceKey:re,header:{showToggleButton:true,customContent:B()},onModeChange:G,enablePersistence:true,initialMode:"bottomSheet",enableGlitchEffects:true,disableScrollWrapper:ie,styles:{},footer:M,footerHeight:ne,children:L}),P]})}function ReduxToolView(s={}){return(0,_jsxRuntime.jsx)(ReduxModal,{...s,inline:true,visible:true,onClose:()=>{}})}const styles=_reactNative.StyleSheet.create({inlineRoot:{flex:1,minHeight:0,backgroundColor:"transparent"},inlineHeader:{paddingHorizontal:12,paddingVertical:8,borderBottomWidth:1,borderBottomColor:_sharedUi.macOSColors.border.default,backgroundColor:"transparent"},container:{flex:1,backgroundColor:"transparent"},headerChipRow:{flexDirection:"row",alignItems:"center",gap:8},headerChip:{flexDirection:"row",alignItems:"center",gap:4,backgroundColor:_sharedUi.night.color.surfaceElevated,paddingHorizontal:10,paddingVertical:5,borderRadius:12,borderWidth:1,borderColor:_sharedUi.night.color.border},headerChipActive:{backgroundColor:_sharedUi.night.color.accentSoft,borderColor:_sharedUi.night.color.accentBorderStrong},headerChipLabel:{fontSize:10,fontWeight:"700",color:_sharedUi.night.color.textTertiary},headerChipValue:{fontSize:12,fontWeight:"600",fontFamily:_sharedUi.monoFont},headerActionButton:{width:32,height:32,borderRadius:8,backgroundColor:_sharedUi.night.color.surfaceElevated,borderWidth:1,borderColor:_sharedUi.night.color.border,alignItems:"center",justifyContent:"center"},headerActionButtonDisabled:{opacity:.55},headerActionButtonActive:{backgroundColor:_sharedUi.night.color.accentSoft,borderColor:_sharedUi.night.color.accentBorderStrong},startButton:{backgroundColor:_sharedUi.macOSColors.semantic.successBackground,borderColor:_sharedUi.macOSColors.semantic.success+"40"},stopButton:{backgroundColor:_sharedUi.macOSColors.semantic.errorBackground,borderColor:_sharedUi.macOSColors.semantic.error+"40"},errorBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:_sharedUi.night.color.dangerSoft,borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.danger+"20"},errorText:{color:_sharedUi.night.color.danger,fontSize:11,flex:1},disabledBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:_sharedUi.night.color.warningSoft,borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.warning+"20"},disabledText:{color:_sharedUi.night.color.warning,fontSize:11,flex:1},listContent:{paddingTop:8,paddingBottom:20},lockedBanner:{flexDirection:"row",alignItems:"center",gap:8,padding:10,marginHorizontal:12,marginTop:8,backgroundColor:_sharedUi.night.color.accentSoft,borderRadius:8,borderWidth:1,borderColor:_sharedUi.night.color.accentBorder},lockedText:{color:_sharedUi.night.color.accent,fontSize:11,fontWeight:"500",flex:1},upgradeBadge:{backgroundColor:_sharedUi.night.color.accent,paddingHorizontal:8,paddingVertical:3,borderRadius:4},upgradeBadgeText:{color:_sharedUi.night.color.onAccent,fontSize:9,fontWeight:"700",letterSpacing:.5}});