@buoy-gg/zustand 2.1.15 → 3.0.0

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