@buoy-gg/debug-borders 2.0.2

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 (43) hide show
  1. package/README.md +334 -0
  2. package/lib/commonjs/debug-borders/components/DebugBordersModal.js +234 -0
  3. package/lib/commonjs/debug-borders/components/DebugBordersStandaloneOverlay.js +436 -0
  4. package/lib/commonjs/debug-borders/index.js +51 -0
  5. package/lib/commonjs/debug-borders/types.js +1 -0
  6. package/lib/commonjs/debug-borders/utils/DebugBordersManager.js +119 -0
  7. package/lib/commonjs/debug-borders/utils/ViewTypeMapper.js +264 -0
  8. package/lib/commonjs/debug-borders/utils/colorGeneration.js +76 -0
  9. package/lib/commonjs/debug-borders/utils/componentInfo.js +183 -0
  10. package/lib/commonjs/debug-borders/utils/componentMeasurement.js +111 -0
  11. package/lib/commonjs/debug-borders/utils/fiberTreeTraversal.js +309 -0
  12. package/lib/commonjs/debug-borders/utils/labelPositioning.js +202 -0
  13. package/lib/commonjs/index.js +34 -0
  14. package/lib/commonjs/package.json +1 -0
  15. package/lib/commonjs/preset.js +178 -0
  16. package/lib/module/debug-borders/components/DebugBordersModal.js +229 -0
  17. package/lib/module/debug-borders/components/DebugBordersStandaloneOverlay.js +432 -0
  18. package/lib/module/debug-borders/index.js +15 -0
  19. package/lib/module/debug-borders/types.js +1 -0
  20. package/lib/module/debug-borders/utils/DebugBordersManager.js +119 -0
  21. package/lib/module/debug-borders/utils/ViewTypeMapper.js +255 -0
  22. package/lib/module/debug-borders/utils/colorGeneration.js +76 -0
  23. package/lib/module/debug-borders/utils/componentInfo.js +183 -0
  24. package/lib/module/debug-borders/utils/componentMeasurement.js +111 -0
  25. package/lib/module/debug-borders/utils/fiberTreeTraversal.js +309 -0
  26. package/lib/module/debug-borders/utils/labelPositioning.js +202 -0
  27. package/lib/module/index.js +7 -0
  28. package/lib/module/preset.js +166 -0
  29. package/lib/typescript/debug-borders/components/DebugBordersModal.d.ts +11 -0
  30. package/lib/typescript/debug-borders/components/DebugBordersModal.d.ts.map +1 -0
  31. package/lib/typescript/debug-borders/components/DebugBordersStandaloneOverlay.d.ts +15 -0
  32. package/lib/typescript/debug-borders/components/DebugBordersStandaloneOverlay.d.ts.map +1 -0
  33. package/lib/typescript/debug-borders/index.d.ts +8 -0
  34. package/lib/typescript/debug-borders/index.d.ts.map +1 -0
  35. package/lib/typescript/debug-borders/types.d.ts +45 -0
  36. package/lib/typescript/debug-borders/types.d.ts.map +1 -0
  37. package/lib/typescript/debug-borders/utils/ViewTypeMapper.d.ts +66 -0
  38. package/lib/typescript/debug-borders/utils/ViewTypeMapper.d.ts.map +1 -0
  39. package/lib/typescript/index.d.ts +3 -0
  40. package/lib/typescript/index.d.ts.map +1 -0
  41. package/lib/typescript/preset.d.ts +108 -0
  42. package/lib/typescript/preset.d.ts.map +1 -0
  43. package/package.json +72 -0
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # @buoy/debug-borders
2
+
3
+ A visual layout debugging tool for React Native that highlights components with colored borders and labels. Features three display modes, smart filtering to show only meaningful components, and tap-to-inspect functionality. Works with Expo, React Native CLI, and supports both the new architecture (Fabric) and legacy architecture.
4
+
5
+ ## Features
6
+
7
+ - **Three Display Modes**: Cycle through Off → Borders → Labels with a single tap
8
+ - **Smart Filtering**: Only shows borders/labels for components with `testID` or `accessibilityLabel`
9
+ - **Tap-to-Inspect**: Tap any label to see detailed component information in a modal
10
+ - **Color-Coded Labels**: Labels match border colors based on identifier type (testID = green, accessibilityLabel = pink)
11
+ - **Label Stacking**: Overlapping labels automatically stack upward like a menu
12
+ - **Hidden Screen Detection**: Automatically hides borders on inactive screens in stack navigators
13
+ - **DevTools Aware**: Hides borders when DevTools modals are open
14
+ - **Real-Time Updates**: Automatically tracks layout changes (updates every 2 seconds)
15
+ - **Touch-Through**: Borders don't interfere with touch interactions
16
+ - **Performance Optimized**: Minimal impact (~30ms to measure 400+ components)
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @buoy/debug-borders
22
+ # or
23
+ pnpm add @buoy/debug-borders
24
+ # or
25
+ yarn add @buoy/debug-borders
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```tsx
31
+ import { debugBordersToolPreset, DebugBordersStandaloneOverlay } from '@buoy/debug-borders';
32
+ import { FloatingDevTools } from '@buoy/core';
33
+
34
+ const installedApps = [
35
+ debugBordersToolPreset, // Add the tool to your menu
36
+ // ...your other tools
37
+ ];
38
+
39
+ function App() {
40
+ return (
41
+ <>
42
+ <FloatingDevTools
43
+ apps={installedApps}
44
+ environment="local"
45
+ userRole="admin"
46
+ />
47
+
48
+ {/* IMPORTANT: Render overlay at root level */}
49
+ <DebugBordersStandaloneOverlay />
50
+
51
+ {/* Your app content */}
52
+ <YourAppContent />
53
+ </>
54
+ );
55
+ }
56
+ ```
57
+
58
+ **Done!** Tap the BORDERS icon in the floating menu to cycle through modes.
59
+
60
+ ## Display Modes
61
+
62
+ The tool cycles through three modes when you tap the icon:
63
+
64
+ | Mode | Icon Color | Description |
65
+ |------|------------|-------------|
66
+ | **Off** | Gray | No borders displayed |
67
+ | **Borders** | Green | Rainbow-colored borders for all components (depth-based colors) |
68
+ | **Labels** | Cyan | Borders + labels only for components with `testID` or `accessibilityLabel` |
69
+
70
+ ### Labels Mode Features
71
+
72
+ In Labels mode, the tool provides focused debugging:
73
+
74
+ - **Only shows components with identifiers** - Components with `testID` or `accessibilityLabel` get borders and labels
75
+ - **Color-coded by identifier type**:
76
+ - 🟢 Green = `testID`
77
+ - 🩷 Pink = `accessibilityLabel`
78
+ - **Labels positioned above boxes** - Easy to read without obscuring content
79
+ - **Automatic stacking** - Overlapping labels stack upward like a menu
80
+ - **Tap to inspect** - Tap any label to see full component details
81
+
82
+ ## Tap-to-Inspect Modal
83
+
84
+ Tap any label in Labels mode to open a detailed inspection modal showing:
85
+
86
+ ### Identifiers
87
+ - `testID` (green)
88
+ - `accessibilityLabel` (pink)
89
+ - `nativeID` (amber)
90
+ - `key`
91
+
92
+ ### Component Info
93
+ - Component Name (the React component that rendered this)
94
+ - Parent Component
95
+ - Display Name (friendly name like "View", "Text")
96
+ - Native View Type (e.g., "RCTView", "RCTText")
97
+ - Fiber Tag
98
+
99
+ ### Position & Size
100
+ - X, Y coordinates
101
+ - Width, Height
102
+ - Depth in component tree
103
+
104
+ ### Accessibility
105
+ - Role
106
+ - Hint
107
+ - State (displayed with interactive DataViewer)
108
+
109
+ ### Styles
110
+ - Full computed styles (displayed with interactive DataViewer)
111
+
112
+ ## Usage Examples
113
+
114
+ ### Basic Usage with Preset
115
+
116
+ ```tsx
117
+ import { debugBordersToolPreset, DebugBordersStandaloneOverlay } from '@buoy/debug-borders';
118
+
119
+ // Add to your FloatingDevTools apps array
120
+ const installedApps = [debugBordersToolPreset];
121
+
122
+ // Render overlay at root level
123
+ <DebugBordersStandaloneOverlay />
124
+
125
+ // Tap the BORDERS icon to cycle: Off → Borders → Labels → Off
126
+ ```
127
+
128
+ ### Custom Configuration
129
+
130
+ ```tsx
131
+ import { createDebugBordersTool, DebugBordersStandaloneOverlay } from '@buoy/debug-borders';
132
+
133
+ const customBordersTool = createDebugBordersTool({
134
+ name: "LAYOUT",
135
+ description: "Layout visualizer",
136
+ offColor: "#9ca3af", // Gray when off
137
+ bordersColor: "#ec4899", // Pink in borders mode
138
+ labelsColor: "#8b5cf6", // Purple in labels mode
139
+ id: "custom-borders",
140
+ });
141
+
142
+ const installedApps = [customBordersTool];
143
+ ```
144
+
145
+ ### Programmatic Control
146
+
147
+ ```tsx
148
+ import { DebugBordersManager, DebugBordersStandaloneOverlay } from '@buoy/debug-borders';
149
+
150
+ // Get current mode
151
+ const mode = DebugBordersManager.getMode(); // "off" | "borders" | "labels"
152
+
153
+ // Set specific mode
154
+ DebugBordersManager.setMode("borders");
155
+ DebugBordersManager.setMode("labels");
156
+ DebugBordersManager.setMode("off");
157
+
158
+ // Cycle to next mode
159
+ DebugBordersManager.cycle(); // off → borders → labels → off
160
+
161
+ // Check if labels should be shown
162
+ const showLabels = DebugBordersManager.showLabels(); // true when mode is "labels"
163
+
164
+ // Subscribe to mode changes
165
+ const unsubscribe = DebugBordersManager.subscribe((mode) => {
166
+ console.log('Mode changed to:', mode);
167
+ });
168
+
169
+ // Clean up
170
+ unsubscribe();
171
+ ```
172
+
173
+ ## API Reference
174
+
175
+ ### Preset
176
+
177
+ #### `debugBordersToolPreset`
178
+
179
+ Pre-configured tool for FloatingDevTools. Shows only in the floating menu (not the dial).
180
+
181
+ ```tsx
182
+ import { debugBordersToolPreset } from '@buoy/debug-borders';
183
+
184
+ const installedApps = [debugBordersToolPreset];
185
+ ```
186
+
187
+ ### Functions
188
+
189
+ #### `createDebugBordersTool(options?)`
190
+
191
+ Create a custom debug borders tool configuration.
192
+
193
+ **Options:**
194
+ - `name?: string` - Tool name (default: "BORDERS")
195
+ - `description?: string` - Tool description
196
+ - `offColor?: string` - Icon color when off (default: "#6b7280" gray)
197
+ - `bordersColor?: string` - Icon color in borders mode (default: "#10b981" green)
198
+ - `labelsColor?: string` - Icon color in labels mode (default: "#06b6d4" cyan)
199
+ - `id?: string` - Custom tool ID (default: "debug-borders")
200
+
201
+ ### Components
202
+
203
+ #### `<DebugBordersStandaloneOverlay />`
204
+
205
+ The main overlay component that renders borders and labels. **Must be rendered at root level.**
206
+
207
+ ```tsx
208
+ import { DebugBordersStandaloneOverlay } from '@buoy/debug-borders';
209
+
210
+ <DebugBordersStandaloneOverlay />
211
+ ```
212
+
213
+ **Note:** This component must be rendered at the root level of your app, outside all modals and navigation containers, for borders to appear on top of everything.
214
+
215
+ ### Manager API
216
+
217
+ #### `DebugBordersManager`
218
+
219
+ Global state manager for controlling debug borders.
220
+
221
+ **Methods:**
222
+ - `getMode(): "off" | "borders" | "labels"` - Get current display mode
223
+ - `setMode(mode)` - Set display mode
224
+ - `cycle()` - Cycle to next mode (off → borders → labels → off)
225
+ - `showLabels(): boolean` - Check if labels should be shown (mode === "labels")
226
+ - `subscribe(callback: (mode) => void): () => void` - Subscribe to mode changes
227
+
228
+ ## How It Works
229
+
230
+ ### Smart Filtering
231
+
232
+ The tool intelligently filters what it displays:
233
+
234
+ 1. **Hidden Screen Detection** - Skips components in inactive screens (React Navigation stack)
235
+ 2. **SVG Filtering** - Excludes SVG elements (RNSVG* components)
236
+ 3. **DevTools Filtering** - Excludes FloatingDevTools, modals, and other dev tool components
237
+ 4. **Identifier Filtering** (Labels mode) - Only shows components with `testID` or `accessibilityLabel`
238
+
239
+ ### Label Positioning
240
+
241
+ Labels are positioned above their component boxes and automatically stack when they would overlap:
242
+
243
+ ```
244
+ ┌─────────────────────────────────────┐
245
+ │ [submitButton] │ ← Labels stack upward
246
+ │ [formContainer] │
247
+ ├─────────────────────────────────────┤
248
+ │ │
249
+ │ Component Content │
250
+ │ │
251
+ └─────────────────────────────────────┘
252
+ ```
253
+
254
+ ### Color Scheme
255
+
256
+ **Borders Mode** - Colors based on nesting depth using golden angle (137.5°):
257
+ | Depth | Color |
258
+ |-------|-------|
259
+ | 0 | Red-Orange |
260
+ | 1 | Green |
261
+ | 2 | Blue |
262
+ | 3+ | Continues with golden angle |
263
+
264
+ **Labels Mode** - Colors based on identifier type:
265
+ | Identifier | Color |
266
+ |------------|-------|
267
+ | testID | Green (#10b981) |
268
+ | accessibilityLabel | Pink (#ec4899) |
269
+ | componentName | Purple (#a855f7) |
270
+ | nativeID | Amber (#f59e0b) |
271
+ | viewType (fallback) | Gray (#6b7280) |
272
+
273
+ ## Performance
274
+
275
+ - **Measurement Time:** ~30ms for 400+ components
276
+ - **Update Frequency:** Every 2 seconds when enabled
277
+ - **Memory Impact:** Minimal (only stores rectangle data)
278
+ - **Runtime Impact:** Zero when disabled
279
+ - **Label Positioning:** O(n²) worst case, optimized with early exits
280
+
281
+ ## Compatibility
282
+
283
+ - React Native >= 0.70.0
284
+ - Fabric (New Architecture)
285
+ - Paper (Legacy Architecture)
286
+ - Expo
287
+ - React Native CLI
288
+
289
+ ## Troubleshooting
290
+
291
+ ### Borders not showing?
292
+
293
+ 1. Make sure `DebugBordersStandaloneOverlay` is rendered at root level
294
+ 2. Tap the icon to cycle modes - you may be in "off" mode
295
+ 3. In Labels mode, borders only show for components with `testID` or `accessibilityLabel`
296
+ 4. Verify you're in development mode (`__DEV__` is true)
297
+
298
+ ### Labels overlapping content?
299
+
300
+ Labels are positioned above component boxes. If they still overlap, they automatically stack upward. Very dense UIs may have many stacked labels.
301
+
302
+ ### Can't tap labels?
303
+
304
+ Make sure you're in Labels mode (cyan icon). In Borders mode, labels aren't shown and tapping doesn't work.
305
+
306
+ ### Modal not scrolling?
307
+
308
+ The inspection modal uses the standard JsModal component. Swipe up to expand it or drag the handle to resize.
309
+
310
+ ## Structure
311
+
312
+ ```
313
+ debug-borders/
314
+ ├── src/
315
+ │ ├── index.tsx # Main exports
316
+ │ ├── preset.tsx # Preset configuration
317
+ │ └── debug-borders/
318
+ │ ├── components/
319
+ │ │ └── DebugBordersStandaloneOverlay.tsx
320
+ │ └── utils/
321
+ │ ├── DebugBordersManager.js # State manager (3 modes)
322
+ │ ├── fiberTreeTraversal.js # Fiber traversal + filtering
323
+ │ ├── componentMeasurement.js # Component measurements
324
+ │ ├── componentInfo.js # Label extraction
325
+ │ ├── labelPositioning.js # Overlap resolution
326
+ │ ├── ViewTypeMapper.ts # Native → friendly names
327
+ │ └── colorGeneration.js # Color generation
328
+ ├── package.json
329
+ └── README.md
330
+ ```
331
+
332
+ ## License
333
+
334
+ MIT
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.DebugBordersModal = DebugBordersModal;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
10
+ var _jsxRuntime = require("react/jsx-runtime");
11
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
12
+ const DebugBordersManager = require("../utils/DebugBordersManager");
13
+
14
+ /**
15
+ * Modal component for controlling debug borders.
16
+ * This allows developers to toggle visual layout debugging borders on/off.
17
+ */
18
+ function DebugBordersModal({
19
+ visible,
20
+ onClose
21
+ }) {
22
+ const [enabled, setEnabled] = (0, _react.useState)(false);
23
+ const [componentCount, setComponentCount] = (0, _react.useState)(0);
24
+
25
+ // Subscribe to manager state
26
+ (0, _react.useEffect)(() => {
27
+ const unsubscribe = DebugBordersManager.subscribe(setEnabled);
28
+ setEnabled(DebugBordersManager.isEnabled());
29
+ return unsubscribe;
30
+ }, []);
31
+
32
+ // Track component count when enabled
33
+ (0, _react.useEffect)(() => {
34
+ if (!enabled) {
35
+ setComponentCount(0);
36
+ return;
37
+ }
38
+
39
+ // Update component count periodically
40
+ const updateCount = () => {
41
+ try {
42
+ const {
43
+ getAllHostComponentInstances
44
+ } = require("../utils/fiberTreeTraversal");
45
+ const instances = getAllHostComponentInstances();
46
+ setComponentCount(instances.length);
47
+ } catch (error) {
48
+ console.error("[DebugBorders] Error getting component count:", error);
49
+ }
50
+ };
51
+
52
+ // Initial count
53
+ setTimeout(updateCount, 600);
54
+
55
+ // Update every 2 seconds
56
+ const interval = setInterval(updateCount, 2000);
57
+ return () => clearInterval(interval);
58
+ }, [enabled]);
59
+ const handleToggle = () => {
60
+ DebugBordersManager.toggle();
61
+ };
62
+ if (!visible) return null;
63
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
64
+ style: styles.container,
65
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
66
+ style: styles.content,
67
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
68
+ style: styles.title,
69
+ children: "Debug Borders"
70
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
71
+ style: styles.description,
72
+ children: "Visualize component layout structure with colored borders"
73
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
74
+ style: styles.statusRow,
75
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
76
+ style: styles.statusLabel,
77
+ children: "Status:"
78
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.StatusBadge, {
79
+ status: enabled ? "active" : "inactive"
80
+ })]
81
+ }), enabled && componentCount > 0 && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
82
+ style: styles.statsRow,
83
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
84
+ style: styles.statsLabel,
85
+ children: "Components Tracked:"
86
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
87
+ style: styles.statsValue,
88
+ children: componentCount
89
+ })]
90
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
91
+ style: styles.toggleContainer,
92
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
93
+ style: [styles.button, enabled && styles.buttonActive],
94
+ onPress: handleToggle,
95
+ accessibilityRole: "button",
96
+ accessibilityLabel: `Debug borders ${enabled ? "enabled" : "disabled"}`,
97
+ accessibilityHint: "Tap to toggle debug borders",
98
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Layers, {
99
+ size: 40,
100
+ color: enabled ? _sharedUi.macOSColors.semantic.success : _sharedUi.macOSColors.text.secondary
101
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
102
+ style: [styles.buttonText, enabled && styles.buttonTextActive],
103
+ children: enabled ? "Disable Borders" : "Enable Borders"
104
+ })]
105
+ })
106
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
107
+ style: styles.infoBox,
108
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.StatusIndicator, {
109
+ status: enabled ? "success" : "info",
110
+ size: "small"
111
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
112
+ style: styles.infoText,
113
+ children: enabled ? "Borders update every 2 seconds. Tap button to disable." : "Tap button to show colored borders around all components."
114
+ })]
115
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
116
+ style: styles.closeButton,
117
+ onPress: onClose,
118
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
119
+ style: styles.closeButtonText,
120
+ children: "Close"
121
+ })
122
+ })]
123
+ })
124
+ });
125
+ }
126
+ const styles = _reactNative.StyleSheet.create({
127
+ container: {
128
+ flex: 1,
129
+ justifyContent: "center",
130
+ alignItems: "center",
131
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
132
+ },
133
+ content: {
134
+ backgroundColor: _sharedUi.macOSColors.background.card,
135
+ borderRadius: 16,
136
+ padding: 32,
137
+ width: "85%",
138
+ maxWidth: 420,
139
+ alignItems: "center"
140
+ },
141
+ title: {
142
+ fontSize: 24,
143
+ fontWeight: "700",
144
+ color: _sharedUi.macOSColors.text.primary,
145
+ marginBottom: 8
146
+ },
147
+ description: {
148
+ fontSize: 14,
149
+ color: _sharedUi.macOSColors.text.secondary,
150
+ textAlign: "center",
151
+ marginBottom: 24
152
+ },
153
+ statusRow: {
154
+ flexDirection: "row",
155
+ alignItems: "center",
156
+ marginBottom: 16,
157
+ gap: 8
158
+ },
159
+ statusLabel: {
160
+ fontSize: 14,
161
+ color: _sharedUi.macOSColors.text.secondary,
162
+ fontWeight: "600"
163
+ },
164
+ statsRow: {
165
+ flexDirection: "row",
166
+ alignItems: "center",
167
+ marginBottom: 16,
168
+ gap: 8
169
+ },
170
+ statsLabel: {
171
+ fontSize: 14,
172
+ color: _sharedUi.macOSColors.text.secondary,
173
+ fontWeight: "600"
174
+ },
175
+ statsValue: {
176
+ fontSize: 16,
177
+ color: _sharedUi.macOSColors.text.primary,
178
+ fontWeight: "700"
179
+ },
180
+ toggleContainer: {
181
+ alignItems: "center",
182
+ justifyContent: "center",
183
+ marginBottom: 24
184
+ },
185
+ button: {
186
+ alignItems: "center",
187
+ justifyContent: "center",
188
+ padding: 24,
189
+ borderRadius: 16,
190
+ backgroundColor: _sharedUi.macOSColors.background.hover,
191
+ borderWidth: 2,
192
+ borderColor: _sharedUi.macOSColors.border.default,
193
+ minWidth: 200
194
+ },
195
+ buttonActive: {
196
+ borderColor: _sharedUi.macOSColors.semantic.success,
197
+ backgroundColor: `${_sharedUi.macOSColors.semantic.success}15`
198
+ },
199
+ buttonText: {
200
+ marginTop: 12,
201
+ fontSize: 16,
202
+ fontWeight: "600",
203
+ color: _sharedUi.macOSColors.text.secondary
204
+ },
205
+ buttonTextActive: {
206
+ color: _sharedUi.macOSColors.semantic.success
207
+ },
208
+ infoBox: {
209
+ flexDirection: "row",
210
+ alignItems: "flex-start",
211
+ backgroundColor: _sharedUi.macOSColors.background.hover,
212
+ padding: 12,
213
+ borderRadius: 8,
214
+ marginBottom: 24,
215
+ gap: 8
216
+ },
217
+ infoText: {
218
+ flex: 1,
219
+ fontSize: 12,
220
+ color: _sharedUi.macOSColors.text.secondary,
221
+ lineHeight: 16
222
+ },
223
+ closeButton: {
224
+ paddingVertical: 12,
225
+ paddingHorizontal: 32,
226
+ backgroundColor: _sharedUi.macOSColors.semantic.info,
227
+ borderRadius: 8
228
+ },
229
+ closeButtonText: {
230
+ color: _sharedUi.macOSColors.text.primary,
231
+ fontSize: 16,
232
+ fontWeight: "600"
233
+ }
234
+ });