@buoy-gg/floating-tools-core 2.2.0 → 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 (111) hide show
  1. package/lib/commonjs/FloatingToolsStore.js +501 -1
  2. package/lib/commonjs/colors.js +54 -1
  3. package/lib/commonjs/constants.js +31 -1
  4. package/lib/commonjs/devToolsState.js +325 -1
  5. package/lib/commonjs/dial.js +617 -1
  6. package/lib/commonjs/dialUsage.js +130 -1
  7. package/lib/commonjs/easing.js +69 -1
  8. package/lib/commonjs/icons/benchmark-icon.js +24 -1
  9. package/lib/commonjs/icons/env-icon.js +24 -1
  10. package/lib/commonjs/icons/events-icon.js +24 -1
  11. package/lib/commonjs/icons/highlight-icon.js +24 -1
  12. package/lib/commonjs/icons/icon-data.js +2268 -1
  13. package/lib/commonjs/icons/icon-factories.js +173 -1
  14. package/lib/commonjs/icons/icon-primitives.js +559 -1
  15. package/lib/commonjs/icons/icon-primitives.native.js +779 -1
  16. package/lib/commonjs/icons/icon-renderer.js +260 -1
  17. package/lib/commonjs/icons/network-icon.js +24 -1
  18. package/lib/commonjs/icons/query-icon.js +24 -1
  19. package/lib/commonjs/icons/redux-icon.js +85 -1
  20. package/lib/commonjs/icons/renders-icon.js +33 -1
  21. package/lib/commonjs/icons/routes-icon.js +49 -1
  22. package/lib/commonjs/icons/sentry-icon.js +24 -1
  23. package/lib/commonjs/icons/storage-icon.js +24 -1
  24. package/lib/commonjs/icons/wifi-icon.js +24 -1
  25. package/lib/commonjs/index.js +760 -1
  26. package/lib/commonjs/settings.js +601 -1
  27. package/lib/commonjs/utils.js +72 -1
  28. package/lib/module/FloatingToolsStore.js +496 -1
  29. package/lib/module/colors.js +49 -1
  30. package/lib/module/constants.js +27 -1
  31. package/lib/module/devToolsState.js +318 -1
  32. package/lib/module/dial.js +603 -1
  33. package/lib/module/dialUsage.js +122 -1
  34. package/lib/module/easing.js +62 -1
  35. package/lib/module/icons/benchmark-icon.js +15 -1
  36. package/lib/module/icons/env-icon.js +15 -1
  37. package/lib/module/icons/events-icon.js +15 -1
  38. package/lib/module/icons/highlight-icon.js +15 -1
  39. package/lib/module/icons/icon-data.js +2264 -1
  40. package/lib/module/icons/icon-factories.js +163 -1
  41. package/lib/module/icons/icon-primitives.js +547 -1
  42. package/lib/module/icons/icon-primitives.native.js +767 -1
  43. package/lib/module/icons/icon-renderer.js +255 -1
  44. package/lib/module/icons/network-icon.js +15 -1
  45. package/lib/module/icons/query-icon.js +15 -1
  46. package/lib/module/icons/redux-icon.js +81 -1
  47. package/lib/module/icons/renders-icon.js +17 -1
  48. package/lib/module/icons/routes-icon.js +41 -1
  49. package/lib/module/icons/sentry-icon.js +15 -1
  50. package/lib/module/icons/storage-icon.js +15 -1
  51. package/lib/module/icons/wifi-icon.js +15 -1
  52. package/lib/module/index.js +106 -1
  53. package/lib/module/settings.js +589 -1
  54. package/lib/module/utils.js +66 -1
  55. package/lib/typescript/commonjs/FloatingToolsStore.d.ts.map +1 -0
  56. package/lib/typescript/commonjs/colors.d.ts.map +1 -0
  57. package/lib/typescript/commonjs/constants.d.ts.map +1 -0
  58. package/lib/typescript/commonjs/devToolsState.d.ts.map +1 -0
  59. package/lib/typescript/commonjs/dial.d.ts.map +1 -0
  60. package/lib/typescript/commonjs/dialUsage.d.ts.map +1 -0
  61. package/lib/typescript/commonjs/easing.d.ts.map +1 -0
  62. package/lib/typescript/commonjs/icons/benchmark-icon.d.ts.map +1 -0
  63. package/lib/typescript/commonjs/icons/env-icon.d.ts.map +1 -0
  64. package/lib/typescript/commonjs/icons/events-icon.d.ts.map +1 -0
  65. package/lib/typescript/commonjs/icons/highlight-icon.d.ts.map +1 -0
  66. package/lib/typescript/commonjs/icons/icon-data.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/icons/icon-factories.d.ts.map +1 -0
  68. package/lib/typescript/commonjs/icons/icon-primitives.d.ts.map +1 -0
  69. package/lib/typescript/commonjs/icons/icon-primitives.native.d.ts.map +1 -0
  70. package/lib/typescript/commonjs/icons/icon-renderer.d.ts.map +1 -0
  71. package/lib/typescript/commonjs/icons/network-icon.d.ts.map +1 -0
  72. package/lib/typescript/commonjs/icons/query-icon.d.ts.map +1 -0
  73. package/lib/typescript/commonjs/icons/redux-icon.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/icons/renders-icon.d.ts.map +1 -0
  75. package/lib/typescript/commonjs/icons/routes-icon.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/icons/sentry-icon.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/icons/storage-icon.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/icons/wifi-icon.d.ts.map +1 -0
  79. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/settings.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/types.d.ts.map +1 -0
  82. package/lib/typescript/commonjs/utils.d.ts.map +1 -0
  83. package/lib/typescript/module/FloatingToolsStore.d.ts.map +1 -0
  84. package/lib/typescript/module/colors.d.ts.map +1 -0
  85. package/lib/typescript/module/constants.d.ts.map +1 -0
  86. package/lib/typescript/module/devToolsState.d.ts.map +1 -0
  87. package/lib/typescript/module/dial.d.ts.map +1 -0
  88. package/lib/typescript/module/dialUsage.d.ts.map +1 -0
  89. package/lib/typescript/module/easing.d.ts.map +1 -0
  90. package/lib/typescript/module/icons/benchmark-icon.d.ts.map +1 -0
  91. package/lib/typescript/module/icons/env-icon.d.ts.map +1 -0
  92. package/lib/typescript/module/icons/events-icon.d.ts.map +1 -0
  93. package/lib/typescript/module/icons/highlight-icon.d.ts.map +1 -0
  94. package/lib/typescript/module/icons/icon-data.d.ts.map +1 -0
  95. package/lib/typescript/module/icons/icon-factories.d.ts.map +1 -0
  96. package/lib/typescript/module/icons/icon-primitives.d.ts.map +1 -0
  97. package/lib/typescript/module/icons/icon-primitives.native.d.ts.map +1 -0
  98. package/lib/typescript/module/icons/icon-renderer.d.ts.map +1 -0
  99. package/lib/typescript/module/icons/network-icon.d.ts.map +1 -0
  100. package/lib/typescript/module/icons/query-icon.d.ts.map +1 -0
  101. package/lib/typescript/module/icons/redux-icon.d.ts.map +1 -0
  102. package/lib/typescript/module/icons/renders-icon.d.ts.map +1 -0
  103. package/lib/typescript/module/icons/routes-icon.d.ts.map +1 -0
  104. package/lib/typescript/module/icons/sentry-icon.d.ts.map +1 -0
  105. package/lib/typescript/module/icons/storage-icon.d.ts.map +1 -0
  106. package/lib/typescript/module/icons/wifi-icon.d.ts.map +1 -0
  107. package/lib/typescript/module/index.d.ts.map +1 -0
  108. package/lib/typescript/module/settings.d.ts.map +1 -0
  109. package/lib/typescript/module/types.d.ts.map +1 -0
  110. package/lib/typescript/module/utils.d.ts.map +1 -0
  111. package/package.json +1 -1
@@ -1 +1,601 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.toolLabels=exports.toolDescriptions=exports.toolColors=exports.settingsTabs=exports.settingsStyles=exports.settingsEventBus=exports.settingsColors=exports.sanitizeFloatingSettings=exports.mergeSettingsWithDefaults=exports.globalSettingsConfig=exports.getToolLabel=exports.getToolDescription=exports.getToolColor=exports.getEnabledToolIds=exports.generateDefaultSettings=exports.enforceDialLimit=exports.countEnabledTools=exports.canEnableDialTool=exports.SettingsEventBus=exports.SETTINGS_STORAGE_KEY=exports.MAX_SETTINGS_DIAL_SLOTS=void 0;var _colors=require("./colors.js");const SETTINGS_STORAGE_KEY=exports.SETTINGS_STORAGE_KEY="@react_buoy_dev_tools_settings",MAX_SETTINGS_DIAL_SLOTS=exports.MAX_SETTINGS_DIAL_SLOTS=6,settingsTabs=exports.settingsTabs=[{key:"floating",label:"FLOATING"},{key:"settings",label:"SETTINGS"},{key:"pro",label:"PRO"}],settingsColors=exports.settingsColors={background:"rgba(8, 12, 21, 0.98)",panel:"rgba(16, 22, 35, 0.98)",backdrop:"rgba(0, 0, 0, 0.85)",cardBackground:"#0F172A",cardBorder:"#25324A",cardBorderHover:"#3b4c6b",text:"#E6EEFF",textSecondary:"#B8BFC9",textMuted:"#7F91B2",textDim:"#7A8599",success:"#4AFF9F",warning:"#FFEB3B",error:"#FF5252",info:"#00B8E6",toggleOnBg:e=>(0,_colors.withAlpha)(e,"33"),toggleOnBorder:e=>(0,_colors.withAlpha)(e,"88"),toggleOffBg:"#1b2334",toggleOffBorder:"#2a3550",toggleOffText:"#8CA2C8",tabActiveBg:"rgba(0, 184, 230, 0.13)",tabActiveBorder:"#00B8E6",tabInactiveBg:"transparent",tabInactiveText:"#7A8599"},toolColors=exports.toolColors={query:"#FF4154",env:"#4AFF9F",sentry:"#FF5252",storage:"#BA68C8",network:"#4AFF9F",wifi:"#4AFF9F",environment:"#4AFF9F","debug-borders":"#10b981","highlight-updates":"#10b981","highlight-updates-modal":"#10b981",benchmark:"#F59E0B","route-events":"#FF9F1C",routes:"#FF9F1C",renders:"#10b981"},getToolColor=e=>toolColors[e]||settingsColors.info;exports.getToolColor=getToolColor;const toolLabels=exports.toolLabels={query:"QUERY",env:"ENV",sentry:"SENTRY",storage:"STORAGE",network:"NET",wifi:"WIFI",environment:"ENV BADGE","debug-borders":"DEBUG BORDERS","highlight-updates":"HIGHLIGHT","highlight-updates-modal":"HIGHLIGHT MODAL",benchmark:"BENCHMARK","route-events":"ROUTES",routes:"ROUTES",renders:"HIGHLIGHT"},getToolLabel=(e,t)=>{if(t){const o=t.find(t=>t.id===e);if(o?.name)return o.name}return toolLabels[e]||e.toUpperCase().replace(/_/g," ").replace(/-/g," ")};exports.getToolLabel=getToolLabel;const toolDescriptions=exports.toolDescriptions={query:"React Query inspector",env:"Environment variables debugger",sentry:"Error tracking & monitoring",storage:"AsyncStorage browser",network:"Network request logger",wifi:"Network status toggle",environment:"Environment badge indicator","debug-borders":"Component layout borders","highlight-updates":"View component render tracking","highlight-updates-modal":"Render count inspector",benchmark:"Performance benchmarks","route-events":"Route tracking & navigation inspector",routes:"Route tracking & navigation inspector",renders:"View component render tracking modal"},getToolDescription=(e,t)=>{if(t){const o=t.find(t=>t.id===e);if(o?.description)return o.description}return toolDescriptions[e]||""};exports.getToolDescription=getToolDescription;const settingsStyles=exports.settingsStyles={card:{borderRadius:999,paddingVertical:10,paddingHorizontal:14,marginBottom:10,borderWidth:1},cardInner:{gap:10},iconContainer:{width:28,height:28},toolName:{fontSize:13,fontWeight:"800"},toolDescription:{fontSize:11},toggle:{paddingHorizontal:12,paddingVertical:6,borderRadius:999,borderWidth:1,fontSize:11,fontWeight:"700"},tab:{paddingHorizontal:8,paddingVertical:5,borderRadius:4,fontSize:12,fontWeight:"600",letterSpacing:.5},sectionHeader:{marginBottom:12,paddingHorizontal:4},sectionTitle:{fontSize:10,fontWeight:"700",letterSpacing:1,textTransform:"uppercase"},scrollContent:{paddingTop:16,paddingBottom:24},section:{marginHorizontal:16,marginBottom:24}},globalSettingsConfig=exports.globalSettingsConfig=[{key:"enableSharedModalDimensions",label:"SHARED MODAL SIZE",category:"MODAL",shortDescription:"Sync dimensions across all tools",fullDescription:"When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.",recommendation:"Keep OFF for the best experience. This allows you to customize each tool's modal size separately. Enable only if you prefer uniform modal sizes across all dev tools."},{key:"expandableWindowControls",label:"EXPAND CONTROLS",category:"MODAL",shortDescription:"iPad-style expandable window buttons",fullDescription:"When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed — similar to iPad window controls. When disabled, buttons are directly tappable at their small size.",recommendation:"Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access (e.g. when using a mouse or simulator)."}],enforceDialLimit=(e,t=MAX_SETTINGS_DIAL_SLOTS)=>{let o=t;const n={};for(const[t,s]of Object.entries(e))s&&o>0?(n[t]=!0,o-=1):n[t]=!1;return n};exports.enforceDialLimit=enforceDialLimit;const sanitizeFloatingSettings=(e,t)=>{const{userStatus:o,environment:n,...s}=e,r=t?Object.entries(s).filter(([e])=>t.includes(e)):Object.entries(s);return{...Object.fromEntries(r),environment:n??!1}};exports.sanitizeFloatingSettings=sanitizeFloatingSettings;const mergeSettingsWithDefaults=(e,t,o)=>{if(!t)return e;const n={...e.dialTools,...t.dialTools??{}},s=o?.allowedDialKeys?Object.entries(n).filter(([e])=>o.allowedDialKeys?.includes(e)):Object.entries(n);return{dialTools:enforceDialLimit(Object.fromEntries(s)),floatingTools:sanitizeFloatingSettings({...e.floatingTools,...t.floatingTools??{}},o?.allowedFloatingKeys),globalSettings:{enableSharedModalDimensions:!1,expandableWindowControls:!0,...t.globalSettings??{}}}};exports.mergeSettingsWithDefaults=mergeSettingsWithDefaults;const generateDefaultSettings=(e=[],t,o)=>{const n={},s={},r=new Set(t??[]),i=new Set(o??[]);for(const t of e){const{id:e,slot:o="both"}=t;"dial"!==o&&"both"!==o||(n[e]=i.has(e)),"row"!==o&&"both"!==o||(s[e]=r.has(e))}return{dialTools:enforceDialLimit(n),floatingTools:{...s,environment:r.has("environment")},globalSettings:{enableSharedModalDimensions:!1,expandableWindowControls:!0}}};exports.generateDefaultSettings=generateDefaultSettings;const countEnabledTools=e=>Object.values(e).filter(Boolean).length;exports.countEnabledTools=countEnabledTools;const canEnableDialTool=(e,t,o=MAX_SETTINGS_DIAL_SLOTS)=>{const n=countEnabledTools(e);return!!e[t]||n<o};exports.canEnableDialTool=canEnableDialTool;const getEnabledToolIds=e=>({floating:Object.entries(e.floatingTools).filter(([e,t])=>t).map(([e])=>e),dial:Object.entries(e.dialTools).filter(([e,t])=>t).map(([e])=>e)});exports.getEnabledToolIds=getEnabledToolIds;class SettingsEventBus{listeners=new Set;emit(e){this.listeners.forEach(t=>{try{t(e)}catch(e){console.error("Error emitting settings event:",e)}})}addListener(e){return this.listeners.add(e),()=>this.listeners.delete(e)}clear(){this.listeners.clear()}get listenerCount(){return this.listeners.size}}exports.SettingsEventBus=SettingsEventBus;const settingsEventBus=exports.settingsEventBus=new SettingsEventBus;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.toolLabels = exports.toolDescriptions = exports.toolColors = exports.settingsTabs = exports.settingsStyles = exports.settingsEventBus = exports.settingsColors = exports.sanitizeFloatingSettings = exports.mergeSettingsWithDefaults = exports.globalSettingsConfig = exports.getToolLabel = exports.getToolDescription = exports.getToolColor = exports.getEnabledToolIds = exports.generateDefaultSettings = exports.enforceDialLimit = exports.countEnabledTools = exports.canEnableDialTool = exports.SettingsEventBus = exports.SETTINGS_STORAGE_KEY = exports.MAX_SETTINGS_DIAL_SLOTS = void 0;
7
+ var _colors = require("./colors.js");
8
+ /**
9
+ * Settings - Shared settings types, constants, and utilities.
10
+ *
11
+ * Pure configuration and logic functions for dev tools settings.
12
+ * No React or platform-specific code.
13
+ *
14
+ * This module provides:
15
+ * - Storage keys and constants
16
+ * - Tab configuration
17
+ * - Tool colors, labels, descriptions
18
+ * - Styling constants for cards and toggles
19
+ * - Settings logic (enforce limits, merge, etc.)
20
+ * - Event bus for cross-component sync
21
+ */
22
+
23
+ // =============================
24
+ // Constants
25
+ // =============================
26
+
27
+ /** Storage key for dev tools settings */
28
+ const SETTINGS_STORAGE_KEY = exports.SETTINGS_STORAGE_KEY = '@react_buoy_dev_tools_settings';
29
+
30
+ /** Maximum number of tools in the dial menu */
31
+ const MAX_SETTINGS_DIAL_SLOTS = exports.MAX_SETTINGS_DIAL_SLOTS = 6;
32
+
33
+ // =============================
34
+ // Tab Configuration
35
+ // =============================
36
+
37
+ /**
38
+ * Tab configuration - shared across mobile and web.
39
+ *
40
+ * Note: the dial no longer has a show/hide tab — dial tools are shown
41
+ * automatically, paginated and ranked by usage.
42
+ */
43
+ const settingsTabs = exports.settingsTabs = [{
44
+ key: 'floating',
45
+ label: 'FLOATING'
46
+ }, {
47
+ key: 'settings',
48
+ label: 'SETTINGS'
49
+ }, {
50
+ key: 'pro',
51
+ label: 'PRO'
52
+ }];
53
+
54
+ // =============================
55
+ // Settings Colors
56
+ // =============================
57
+
58
+ /**
59
+ * Color palette for the settings modal.
60
+ * Matches the mobile implementation for consistency.
61
+ */
62
+ const settingsColors = exports.settingsColors = {
63
+ // Base colors
64
+ background: 'rgba(8, 12, 21, 0.98)',
65
+ panel: 'rgba(16, 22, 35, 0.98)',
66
+ backdrop: 'rgba(0, 0, 0, 0.85)',
67
+ // Card colors
68
+ cardBackground: '#0F172A',
69
+ cardBorder: '#25324A',
70
+ cardBorderHover: '#3b4c6b',
71
+ // Text colors
72
+ text: '#E6EEFF',
73
+ textSecondary: '#B8BFC9',
74
+ textMuted: '#7F91B2',
75
+ textDim: '#7A8599',
76
+ // Status colors
77
+ success: '#4AFF9F',
78
+ warning: '#FFEB3B',
79
+ error: '#FF5252',
80
+ info: '#00B8E6',
81
+ // Toggle states
82
+ toggleOnBg: color => (0, _colors.withAlpha)(color, '33'),
83
+ toggleOnBorder: color => (0, _colors.withAlpha)(color, '88'),
84
+ toggleOffBg: '#1b2334',
85
+ toggleOffBorder: '#2a3550',
86
+ toggleOffText: '#8CA2C8',
87
+ // Tab colors
88
+ tabActiveBg: 'rgba(0, 184, 230, 0.13)',
89
+ tabActiveBorder: '#00B8E6',
90
+ tabInactiveBg: 'transparent',
91
+ tabInactiveText: '#7A8599'
92
+ };
93
+ // =============================
94
+ // Tool Colors
95
+ // =============================
96
+
97
+ /**
98
+ * Color mapping for each tool type.
99
+ * Used for card accents, toggle states, and icons.
100
+ */
101
+ const toolColors = exports.toolColors = {
102
+ // Core tools
103
+ query: '#FF4154',
104
+ // React Query red
105
+ env: '#4AFF9F',
106
+ // Green
107
+ sentry: '#FF5252',
108
+ // Red
109
+ storage: '#BA68C8',
110
+ // Purple
111
+ network: '#4AFF9F',
112
+ // Green (matches NETWORK_ICON_COLOR in shared-ui)
113
+ wifi: '#4AFF9F',
114
+ // Green
115
+
116
+ // Display tools
117
+ environment: '#4AFF9F',
118
+ // Green
119
+ 'debug-borders': '#10b981',
120
+ // Emerald
121
+ 'highlight-updates': '#10b981',
122
+ // Emerald
123
+ 'highlight-updates-modal': '#10b981',
124
+ // Emerald
125
+
126
+ // Performance
127
+ benchmark: '#F59E0B',
128
+ // Amber
129
+
130
+ // Navigation
131
+ 'route-events': '#FF9F1C',
132
+ // Orange
133
+ routes: '#FF9F1C',
134
+ // Orange
135
+
136
+ // Render tracking
137
+ renders: '#10b981' // Emerald
138
+ };
139
+
140
+ /**
141
+ * Get the color for a specific tool.
142
+ *
143
+ * @param toolId - Tool identifier
144
+ * @returns Color string for the tool
145
+ */
146
+ const getToolColor = toolId => {
147
+ return toolColors[toolId] || settingsColors.info;
148
+ };
149
+
150
+ // =============================
151
+ // Tool Labels
152
+ // =============================
153
+
154
+ /**
155
+ * Display labels for each tool.
156
+ * These are shown in the settings card headers.
157
+ */
158
+ exports.getToolColor = getToolColor;
159
+ const toolLabels = exports.toolLabels = {
160
+ query: 'QUERY',
161
+ env: 'ENV',
162
+ sentry: 'SENTRY',
163
+ storage: 'STORAGE',
164
+ network: 'NET',
165
+ wifi: 'WIFI',
166
+ environment: 'ENV BADGE',
167
+ 'debug-borders': 'DEBUG BORDERS',
168
+ 'highlight-updates': 'HIGHLIGHT',
169
+ 'highlight-updates-modal': 'HIGHLIGHT MODAL',
170
+ benchmark: 'BENCHMARK',
171
+ 'route-events': 'ROUTES',
172
+ routes: 'ROUTES',
173
+ renders: 'HIGHLIGHT'
174
+ };
175
+
176
+ /**
177
+ * Get the display label for a tool.
178
+ *
179
+ * @param toolId - Tool identifier
180
+ * @param availableApps - Optional list of available apps to check for custom names
181
+ * @returns Display label for the tool
182
+ */
183
+ const getToolLabel = (toolId, availableApps) => {
184
+ // Check available apps first for custom names
185
+ if (availableApps) {
186
+ const app = availableApps.find(a => a.id === toolId);
187
+ if (app?.name) return app.name;
188
+ }
189
+
190
+ // Use predefined label or format from ID
191
+ return toolLabels[toolId] || toolId.toUpperCase().replace(/_/g, ' ').replace(/-/g, ' ');
192
+ };
193
+
194
+ // =============================
195
+ // Tool Descriptions
196
+ // =============================
197
+
198
+ /**
199
+ * Descriptions for each tool.
200
+ * These are shown in the settings card subtitles.
201
+ */
202
+ exports.getToolLabel = getToolLabel;
203
+ const toolDescriptions = exports.toolDescriptions = {
204
+ query: 'React Query inspector',
205
+ env: 'Environment variables debugger',
206
+ sentry: 'Error tracking & monitoring',
207
+ storage: 'AsyncStorage browser',
208
+ network: 'Network request logger',
209
+ wifi: 'Network status toggle',
210
+ environment: 'Environment badge indicator',
211
+ 'debug-borders': 'Component layout borders',
212
+ 'highlight-updates': 'View component render tracking',
213
+ 'highlight-updates-modal': 'Render count inspector',
214
+ benchmark: 'Performance benchmarks',
215
+ 'route-events': 'Route tracking & navigation inspector',
216
+ routes: 'Route tracking & navigation inspector',
217
+ renders: 'View component render tracking modal'
218
+ };
219
+
220
+ /**
221
+ * Get the description for a tool.
222
+ *
223
+ * @param toolId - Tool identifier
224
+ * @param availableApps - Optional list of available apps to check for custom descriptions
225
+ * @returns Description for the tool
226
+ */
227
+ const getToolDescription = (toolId, availableApps) => {
228
+ // Check available apps first for custom descriptions
229
+ if (availableApps) {
230
+ const app = availableApps.find(a => a.id === toolId);
231
+ if (app?.description) return app.description;
232
+ }
233
+
234
+ // Use predefined description
235
+ return toolDescriptions[toolId] || '';
236
+ };
237
+
238
+ // =============================
239
+ // Settings Styling
240
+ // =============================
241
+
242
+ /**
243
+ * Styling configuration for settings UI elements.
244
+ * All numeric values that affect appearance.
245
+ */
246
+ exports.getToolDescription = getToolDescription;
247
+ const settingsStyles = exports.settingsStyles = {
248
+ // Card styling
249
+ card: {
250
+ borderRadius: 999,
251
+ // Pill shape
252
+ paddingVertical: 10,
253
+ paddingHorizontal: 14,
254
+ marginBottom: 10,
255
+ borderWidth: 1
256
+ },
257
+ // Card inner layout
258
+ cardInner: {
259
+ gap: 10
260
+ },
261
+ // Icon container
262
+ iconContainer: {
263
+ width: 28,
264
+ height: 28
265
+ },
266
+ // Tool name text
267
+ toolName: {
268
+ fontSize: 13,
269
+ fontWeight: '800'
270
+ },
271
+ // Tool description text
272
+ toolDescription: {
273
+ fontSize: 11
274
+ },
275
+ // Toggle pill button
276
+ toggle: {
277
+ paddingHorizontal: 12,
278
+ paddingVertical: 6,
279
+ borderRadius: 999,
280
+ borderWidth: 1,
281
+ fontSize: 11,
282
+ fontWeight: '700'
283
+ },
284
+ // Tab styling
285
+ tab: {
286
+ paddingHorizontal: 8,
287
+ paddingVertical: 5,
288
+ borderRadius: 4,
289
+ fontSize: 12,
290
+ fontWeight: '600',
291
+ letterSpacing: 0.5
292
+ },
293
+ // Section header
294
+ sectionHeader: {
295
+ marginBottom: 12,
296
+ paddingHorizontal: 4
297
+ },
298
+ // Section title
299
+ sectionTitle: {
300
+ fontSize: 10,
301
+ fontWeight: '700',
302
+ letterSpacing: 1,
303
+ textTransform: 'uppercase'
304
+ },
305
+ // Scroll content
306
+ scrollContent: {
307
+ paddingTop: 16,
308
+ paddingBottom: 24
309
+ },
310
+ // Section margins
311
+ section: {
312
+ marginHorizontal: 16,
313
+ marginBottom: 24
314
+ }
315
+ };
316
+
317
+ // =============================
318
+ // Global Settings Configuration
319
+ // =============================
320
+
321
+ /**
322
+ * Configuration for global settings cards.
323
+ */
324
+ const globalSettingsConfig = exports.globalSettingsConfig = [{
325
+ key: 'enableSharedModalDimensions',
326
+ label: 'SHARED MODAL SIZE',
327
+ category: 'MODAL',
328
+ shortDescription: 'Sync dimensions across all tools',
329
+ fullDescription: 'When enabled, all tool modals will share the same size and position. Resizing one modal will affect all others. When disabled, each tool remembers its own size and position independently.',
330
+ recommendation: 'Keep OFF for the best experience. This allows you to customize each tool\'s modal size separately. Enable only if you prefer uniform modal sizes across all dev tools.'
331
+ }, {
332
+ key: 'expandableWindowControls',
333
+ label: 'EXPAND CONTROLS',
334
+ category: 'MODAL',
335
+ shortDescription: 'iPad-style expandable window buttons',
336
+ fullDescription: 'When enabled, the window control buttons (minimize, toggle mode, close) start as small dots and expand into larger, easy-to-tap buttons when pressed — similar to iPad window controls. When disabled, buttons are directly tappable at their small size.',
337
+ recommendation: 'Keep ON for touch devices where the small buttons are hard to press. Turn OFF if you prefer direct single-tap access (e.g. when using a mouse or simulator).'
338
+ }];
339
+
340
+ // =============================
341
+ // Types
342
+ // =============================
343
+
344
+ /**
345
+ * Global settings that apply to all dev tools.
346
+ * These settings override individual tool configurations.
347
+ */
348
+
349
+ /**
350
+ * Serialized preferences that power the floating dev tools UI.
351
+ * Values persist across sessions via storage so teams can tailor
352
+ * which tools appear in the dial or floating row.
353
+ */
354
+
355
+ /**
356
+ * Configuration for an available tool/app.
357
+ * Note: name and description are optional - if not provided,
358
+ * getToolLabel() and getToolDescription() will use shared defaults.
359
+ */
360
+
361
+ /**
362
+ * Tool metadata for display in settings.
363
+ */
364
+
365
+ // =============================
366
+ // Utility Functions
367
+ // =============================
368
+
369
+ /**
370
+ * Enforces the dial slot limit by enabling only the first N tools.
371
+ * Tools are processed in order, so tools appearing first in the Record
372
+ * have priority when the limit is reached.
373
+ *
374
+ * @param dialTools - Map of tool ids to enabled state
375
+ * @param maxSlots - Maximum number of enabled tools (default: MAX_SETTINGS_DIAL_SLOTS)
376
+ * @returns New record with limit enforced
377
+ */
378
+ const enforceDialLimit = (dialTools, maxSlots = MAX_SETTINGS_DIAL_SLOTS) => {
379
+ let remaining = maxSlots;
380
+ const limited = {};
381
+ for (const [id, enabled] of Object.entries(dialTools)) {
382
+ if (enabled && remaining > 0) {
383
+ limited[id] = true;
384
+ remaining -= 1;
385
+ } else {
386
+ limited[id] = false;
387
+ }
388
+ }
389
+ return limited;
390
+ };
391
+
392
+ /**
393
+ * Sanitizes floating tools settings by removing unknown keys and ensuring
394
+ * the environment field is always present.
395
+ *
396
+ * @param floating - Floating tools settings object
397
+ * @param allowedKeys - Optional list of allowed tool keys (environment is always allowed)
398
+ * @returns Sanitized floating tools settings
399
+ */
400
+ exports.enforceDialLimit = enforceDialLimit;
401
+ const sanitizeFloatingSettings = (floating, allowedKeys) => {
402
+ const {
403
+ userStatus,
404
+ environment,
405
+ ...rest
406
+ } = floating;
407
+ const filteredEntries = allowedKeys ? Object.entries(rest).filter(([key]) => allowedKeys.includes(key)) : Object.entries(rest);
408
+ return {
409
+ ...Object.fromEntries(filteredEntries),
410
+ environment: environment ?? false
411
+ };
412
+ };
413
+
414
+ /**
415
+ * Options for merging settings with defaults.
416
+ */
417
+ exports.sanitizeFloatingSettings = sanitizeFloatingSettings;
418
+ /**
419
+ * Merges stored settings with defaults, ensuring all required fields exist
420
+ * and limits are enforced.
421
+ *
422
+ * @param defaults - Default settings to use as base
423
+ * @param stored - Stored settings to merge (optional)
424
+ * @param options - Optional filtering options
425
+ * @returns Merged settings with all fields and limits enforced
426
+ */
427
+ const mergeSettingsWithDefaults = (defaults, stored, options) => {
428
+ if (!stored) return defaults;
429
+ const combinedDial = {
430
+ ...defaults.dialTools,
431
+ ...(stored.dialTools ?? {})
432
+ };
433
+ const dialEntries = options?.allowedDialKeys ? Object.entries(combinedDial).filter(([key]) => options.allowedDialKeys?.includes(key)) : Object.entries(combinedDial);
434
+ const mergedDial = enforceDialLimit(Object.fromEntries(dialEntries));
435
+ return {
436
+ dialTools: mergedDial,
437
+ floatingTools: sanitizeFloatingSettings({
438
+ ...defaults.floatingTools,
439
+ ...(stored.floatingTools ?? {})
440
+ }, options?.allowedFloatingKeys),
441
+ globalSettings: {
442
+ enableSharedModalDimensions: false,
443
+ // Default to false
444
+ expandableWindowControls: true,
445
+ // Default to true - expandable on touch devices
446
+ ...(stored.globalSettings ?? {})
447
+ }
448
+ };
449
+ };
450
+
451
+ /**
452
+ * Generates default settings based on available tools and optional team defaults.
453
+ *
454
+ * @param availableTools - List of available tools from auto-discovery
455
+ * @param defaultFloatingTools - Optional array of tool IDs to enable by default in floating bubble
456
+ * @param defaultDialTools - Optional array of tool IDs to enable by default in dial menu
457
+ * @returns Default settings with specified tools enabled
458
+ */
459
+ exports.mergeSettingsWithDefaults = mergeSettingsWithDefaults;
460
+ const generateDefaultSettings = (availableTools = [], defaultFloatingTools, defaultDialTools) => {
461
+ const dialDefaults = {};
462
+ const floatingDefaults = {};
463
+
464
+ // Create sets for quick lookup of default-enabled tools
465
+ const enabledFloatingSet = new Set(defaultFloatingTools ?? []);
466
+ const enabledDialSet = new Set(defaultDialTools ?? []);
467
+ for (const tool of availableTools) {
468
+ const {
469
+ id,
470
+ slot = 'both'
471
+ } = tool;
472
+ if (slot === 'dial' || slot === 'both') {
473
+ // Enable if in defaultDialTools, otherwise false
474
+ dialDefaults[id] = enabledDialSet.has(id);
475
+ }
476
+ if (slot === 'row' || slot === 'both') {
477
+ // Enable if in defaultFloatingTools, otherwise false
478
+ floatingDefaults[id] = enabledFloatingSet.has(id);
479
+ }
480
+ }
481
+ return {
482
+ dialTools: enforceDialLimit(dialDefaults),
483
+ floatingTools: {
484
+ ...floatingDefaults,
485
+ // Special: environment badge - check if 'environment' is in the floating defaults
486
+ environment: enabledFloatingSet.has('environment')
487
+ },
488
+ globalSettings: {
489
+ enableSharedModalDimensions: false,
490
+ // Default to false - each modal has its own persistence
491
+ expandableWindowControls: true // Default to true - expandable on touch devices
492
+ }
493
+ };
494
+ };
495
+
496
+ /**
497
+ * Counts the number of enabled tools in a settings record.
498
+ *
499
+ * @param tools - Record of tool ids to enabled state
500
+ * @returns Count of enabled tools
501
+ */
502
+ exports.generateDefaultSettings = generateDefaultSettings;
503
+ const countEnabledTools = tools => {
504
+ return Object.values(tools).filter(Boolean).length;
505
+ };
506
+
507
+ /**
508
+ * Checks if a dial tool can be enabled (respects slot limit).
509
+ *
510
+ * @param dialTools - Current dial tools settings
511
+ * @param toolId - ID of the tool to check
512
+ * @param maxSlots - Maximum number of slots (default: MAX_SETTINGS_DIAL_SLOTS)
513
+ * @returns true if the tool can be enabled
514
+ */
515
+ exports.countEnabledTools = countEnabledTools;
516
+ const canEnableDialTool = (dialTools, toolId, maxSlots = MAX_SETTINGS_DIAL_SLOTS) => {
517
+ const currentEnabled = countEnabledTools(dialTools);
518
+ const isCurrentlyEnabled = dialTools[toolId];
519
+
520
+ // If already enabled, it can stay enabled
521
+ if (isCurrentlyEnabled) return true;
522
+
523
+ // If not enabled and at limit, cannot enable
524
+ return currentEnabled < maxSlots;
525
+ };
526
+
527
+ /**
528
+ * Extracts enabled tool IDs from a settings record.
529
+ *
530
+ * @param settings - DevTools settings
531
+ * @returns Object with arrays of enabled floating and dial tool IDs
532
+ */
533
+ exports.canEnableDialTool = canEnableDialTool;
534
+ const getEnabledToolIds = settings => {
535
+ const enabledFloating = Object.entries(settings.floatingTools).filter(([_, enabled]) => enabled).map(([id]) => id);
536
+ const enabledDial = Object.entries(settings.dialTools).filter(([_, enabled]) => enabled).map(([id]) => id);
537
+ return {
538
+ floating: enabledFloating,
539
+ dial: enabledDial
540
+ };
541
+ };
542
+
543
+ // =============================
544
+ // Settings Event Bus
545
+ // =============================
546
+ exports.getEnabledToolIds = getEnabledToolIds;
547
+ /**
548
+ * Generic event bus for broadcasting settings changes.
549
+ * Platform-agnostic implementation for synchronizing settings
550
+ * across components.
551
+ */
552
+ class SettingsEventBus {
553
+ listeners = new Set();
554
+
555
+ /**
556
+ * Emit a settings change to all listeners.
557
+ *
558
+ * @param payload - The new settings value
559
+ */
560
+ emit(payload) {
561
+ this.listeners.forEach(listener => {
562
+ try {
563
+ listener(payload);
564
+ } catch (e) {
565
+ console.error('Error emitting settings event:', e);
566
+ }
567
+ });
568
+ }
569
+
570
+ /**
571
+ * Add a listener for settings changes.
572
+ *
573
+ * @param listener - Callback function to invoke on changes
574
+ * @returns Unsubscribe function
575
+ */
576
+ addListener(listener) {
577
+ this.listeners.add(listener);
578
+ return () => this.listeners.delete(listener);
579
+ }
580
+
581
+ /**
582
+ * Remove all listeners.
583
+ */
584
+ clear() {
585
+ this.listeners.clear();
586
+ }
587
+
588
+ /**
589
+ * Get the number of active listeners.
590
+ */
591
+ get listenerCount() {
592
+ return this.listeners.size;
593
+ }
594
+ }
595
+
596
+ /**
597
+ * Default settings event bus instance.
598
+ * Use this for broadcasting settings changes across the app.
599
+ */
600
+ exports.SettingsEventBus = SettingsEventBus;
601
+ const settingsEventBus = exports.settingsEventBus = new SettingsEventBus();
@@ -1 +1,72 @@
1
- "use strict";function getUserStatusConfig(t,e){switch(t){case"admin":return{label:"Admin",dotColor:e.success,textColor:e.success};case"internal":return{label:"Internal",dotColor:e.optional,textColor:e.optional};default:return{label:"User",dotColor:e.muted,textColor:e.secondary}}}function getGripIconLayout(t){return{dotSize:Math.max(2,Math.round(t/6)),columnGap:Math.max(2,Math.round(t/12)),rowGap:Math.max(2,Math.round(t/12))}}function filterValidChildren(t){return t.filter(t=>null!=t&&!1!==t)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.filterValidChildren=filterValidChildren,exports.getGripIconLayout=getGripIconLayout,exports.getUserStatusConfig=getUserStatusConfig;
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.filterValidChildren = filterValidChildren;
7
+ exports.getGripIconLayout = getGripIconLayout;
8
+ exports.getUserStatusConfig = getUserStatusConfig;
9
+ /**
10
+ * Shared utility functions for floating tools.
11
+ * Platform-agnostic - no React or DOM dependencies.
12
+ */
13
+
14
+ // =============================
15
+ // User Status Configuration
16
+ // =============================
17
+
18
+ /**
19
+ * Get display configuration for a user role.
20
+ * Used by both web and mobile renderers.
21
+ */
22
+ function getUserStatusConfig(userRole, colors) {
23
+ switch (userRole) {
24
+ case 'admin':
25
+ return {
26
+ label: 'Admin',
27
+ dotColor: colors.success,
28
+ textColor: colors.success
29
+ };
30
+ case 'internal':
31
+ return {
32
+ label: 'Internal',
33
+ dotColor: colors.optional,
34
+ textColor: colors.optional
35
+ };
36
+ case 'user':
37
+ default:
38
+ return {
39
+ label: 'User',
40
+ dotColor: colors.muted,
41
+ textColor: colors.secondary
42
+ };
43
+ }
44
+ }
45
+
46
+ // =============================
47
+ // Grip Icon Layout
48
+ // =============================
49
+
50
+ /**
51
+ * Calculate grip icon layout dimensions based on size.
52
+ * Creates a 2x3 dot grid pattern.
53
+ */
54
+ function getGripIconLayout(size) {
55
+ return {
56
+ dotSize: Math.max(2, Math.round(size / 6)),
57
+ columnGap: Math.max(2, Math.round(size / 12)),
58
+ rowGap: Math.max(2, Math.round(size / 12))
59
+ };
60
+ }
61
+
62
+ // =============================
63
+ // Children Utilities
64
+ // =============================
65
+
66
+ /**
67
+ * Filter valid React children (removes null, undefined, false).
68
+ * Works with both web and React Native.
69
+ */
70
+ function filterValidChildren(children) {
71
+ return children.filter(child => child != null && child !== false);
72
+ }