@buoy-gg/floating-tools-core 2.1.9 → 2.1.11

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