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