@buoy-gg/route-events 1.7.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 (79) hide show
  1. package/README.md +654 -0
  2. package/lib/commonjs/RouteObserver.js +54 -0
  3. package/lib/commonjs/RouteParser.js +310 -0
  4. package/lib/commonjs/RouteTracker.js +39 -0
  5. package/lib/commonjs/components/NavigationStack.js +584 -0
  6. package/lib/commonjs/components/RouteEventDetailContent.js +492 -0
  7. package/lib/commonjs/components/RouteEventExpandedContent.js +187 -0
  8. package/lib/commonjs/components/RouteEventItemCompact.js +175 -0
  9. package/lib/commonjs/components/RouteEventsModalWithTabs.js +560 -0
  10. package/lib/commonjs/components/RouteEventsTimeline.js +82 -0
  11. package/lib/commonjs/components/RouteFilterViewV2.js +42 -0
  12. package/lib/commonjs/components/RoutesSitemap.js +948 -0
  13. package/lib/commonjs/expoRouterStore.js +104 -0
  14. package/lib/commonjs/index.js +99 -0
  15. package/lib/commonjs/package.json +1 -0
  16. package/lib/commonjs/preset.js +83 -0
  17. package/lib/commonjs/useNavigationStack.js +241 -0
  18. package/lib/commonjs/useRouteObserver.js +73 -0
  19. package/lib/commonjs/useRouteSitemap.js +234 -0
  20. package/lib/commonjs/utils/safeExpoRouter.js +129 -0
  21. package/lib/commonjs/utils/safeReactNavigation.js +104 -0
  22. package/lib/module/RouteObserver.js +49 -0
  23. package/lib/module/RouteParser.js +305 -0
  24. package/lib/module/RouteTracker.js +35 -0
  25. package/lib/module/components/NavigationStack.js +580 -0
  26. package/lib/module/components/RouteEventDetailContent.js +487 -0
  27. package/lib/module/components/RouteEventExpandedContent.js +183 -0
  28. package/lib/module/components/RouteEventItemCompact.js +171 -0
  29. package/lib/module/components/RouteEventsModalWithTabs.js +557 -0
  30. package/lib/module/components/RouteEventsTimeline.js +78 -0
  31. package/lib/module/components/RouteFilterViewV2.js +38 -0
  32. package/lib/module/components/RoutesSitemap.js +944 -0
  33. package/lib/module/expoRouterStore.js +98 -0
  34. package/lib/module/index.js +23 -0
  35. package/lib/module/preset.js +79 -0
  36. package/lib/module/useNavigationStack.js +238 -0
  37. package/lib/module/useRouteObserver.js +70 -0
  38. package/lib/module/useRouteSitemap.js +229 -0
  39. package/lib/module/utils/safeExpoRouter.js +120 -0
  40. package/lib/module/utils/safeReactNavigation.js +98 -0
  41. package/lib/typescript/RouteObserver.d.ts +37 -0
  42. package/lib/typescript/RouteObserver.d.ts.map +1 -0
  43. package/lib/typescript/RouteParser.d.ts +129 -0
  44. package/lib/typescript/RouteParser.d.ts.map +1 -0
  45. package/lib/typescript/RouteTracker.d.ts +29 -0
  46. package/lib/typescript/RouteTracker.d.ts.map +1 -0
  47. package/lib/typescript/components/NavigationStack.d.ts +11 -0
  48. package/lib/typescript/components/NavigationStack.d.ts.map +1 -0
  49. package/lib/typescript/components/RouteEventDetailContent.d.ts +21 -0
  50. package/lib/typescript/components/RouteEventDetailContent.d.ts.map +1 -0
  51. package/lib/typescript/components/RouteEventExpandedContent.d.ts +16 -0
  52. package/lib/typescript/components/RouteEventExpandedContent.d.ts.map +1 -0
  53. package/lib/typescript/components/RouteEventItemCompact.d.ts +15 -0
  54. package/lib/typescript/components/RouteEventItemCompact.d.ts.map +1 -0
  55. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts +15 -0
  56. package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +1 -0
  57. package/lib/typescript/components/RouteEventsTimeline.d.ts +17 -0
  58. package/lib/typescript/components/RouteEventsTimeline.d.ts.map +1 -0
  59. package/lib/typescript/components/RouteFilterViewV2.d.ts +9 -0
  60. package/lib/typescript/components/RouteFilterViewV2.d.ts.map +1 -0
  61. package/lib/typescript/components/RoutesSitemap.d.ts +15 -0
  62. package/lib/typescript/components/RoutesSitemap.d.ts.map +1 -0
  63. package/lib/typescript/expoRouterStore.d.ts +28 -0
  64. package/lib/typescript/expoRouterStore.d.ts.map +1 -0
  65. package/lib/typescript/index.d.ts +18 -0
  66. package/lib/typescript/index.d.ts.map +1 -0
  67. package/lib/typescript/preset.d.ts +76 -0
  68. package/lib/typescript/preset.d.ts.map +1 -0
  69. package/lib/typescript/useNavigationStack.d.ts +48 -0
  70. package/lib/typescript/useNavigationStack.d.ts.map +1 -0
  71. package/lib/typescript/useRouteObserver.d.ts +27 -0
  72. package/lib/typescript/useRouteObserver.d.ts.map +1 -0
  73. package/lib/typescript/useRouteSitemap.d.ts +102 -0
  74. package/lib/typescript/useRouteSitemap.d.ts.map +1 -0
  75. package/lib/typescript/utils/safeExpoRouter.d.ts +13 -0
  76. package/lib/typescript/utils/safeExpoRouter.d.ts.map +1 -0
  77. package/lib/typescript/utils/safeReactNavigation.d.ts +10 -0
  78. package/lib/typescript/utils/safeReactNavigation.d.ts.map +1 -0
  79. package/package.json +72 -0
@@ -0,0 +1,584 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NavigationStack = NavigationStack;
7
+ var _react = require("react");
8
+ var _reactNative = require("react-native");
9
+ var _sharedUi = require("@buoy-gg/shared-ui");
10
+ var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
11
+ var _useNavigationStack = require("../useNavigationStack");
12
+ var _expoRouter = require("expo-router");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ /**
15
+ * NavigationStack - Visual representation of current navigation stack
16
+ *
17
+ * Shows all screens currently mounted in memory, which one is visible,
18
+ * and provides controls to manipulate the stack.
19
+ */
20
+
21
+ // Lazy load the license hooks to avoid circular dependencies
22
+ let _useIsPro = null;
23
+ let _licenseLoadAttempted = false;
24
+ function loadLicenseModule() {
25
+ if (_licenseLoadAttempted) return;
26
+ _licenseLoadAttempted = true;
27
+ try {
28
+ const mod = require("@buoy-gg/license");
29
+ if (mod) {
30
+ _useIsPro = mod.useIsPro ?? null;
31
+ }
32
+ } catch {
33
+ // License package not available
34
+ }
35
+ }
36
+ function getUseIsPro() {
37
+ loadLicenseModule();
38
+ return _useIsPro ?? (() => false);
39
+ }
40
+
41
+ // ============================================================================
42
+ // Types
43
+ // ============================================================================
44
+
45
+ // ============================================================================
46
+ // Main Component
47
+ // ============================================================================
48
+
49
+ function NavigationStack({
50
+ style
51
+ }) {
52
+ const {
53
+ stack,
54
+ focusedRoute,
55
+ stackDepth,
56
+ isAtRoot,
57
+ isLoaded,
58
+ error,
59
+ navigateToIndex,
60
+ goBack,
61
+ popToTop
62
+ } = (0, _useNavigationStack.useNavigationStack)();
63
+ const router = (0, _expoRouter.useRouter)();
64
+ const [expandedIndex, setExpandedIndex] = (0, _react.useState)(null);
65
+ const [showHelp, setShowHelp] = (0, _react.useState)(false);
66
+ const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
67
+
68
+ // Check Pro status internally
69
+ const useIsPro = getUseIsPro();
70
+ const isPro = useIsPro();
71
+
72
+ // ✅ All hooks must be called BEFORE any conditional returns
73
+ // Prepare stack data for copying
74
+ const stackDataForCopy = (0, _react.useMemo)(() => {
75
+ const stackData = stack.map(item => ({
76
+ pathname: item.pathname,
77
+ name: item.name,
78
+ params: item.params,
79
+ isFocused: item.isFocused
80
+ }));
81
+ return JSON.stringify(stackData, null, 2);
82
+ }, [stack]);
83
+
84
+ // Determine which route actions should operate on
85
+ // If a stack item is expanded, actions target that route
86
+ // Otherwise, actions target the focused (visible) route
87
+ const selectedRoute = (0, _react.useMemo)(() => {
88
+ if (expandedIndex !== null && stack[expandedIndex]) {
89
+ return stack[expandedIndex];
90
+ }
91
+ return focusedRoute;
92
+ }, [expandedIndex, stack, focusedRoute]);
93
+ const isNonFocusedSelected = (0, _react.useMemo)(() => {
94
+ return expandedIndex !== null && stack[expandedIndex] && !stack[expandedIndex].isFocused;
95
+ }, [expandedIndex, stack]);
96
+
97
+ // Loading state
98
+ if (!isLoaded) {
99
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
100
+ style: [styles.container, style],
101
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
102
+ style: styles.loadingContainer,
103
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
104
+ style: styles.loadingText,
105
+ children: "Loading navigation stack..."
106
+ })
107
+ })
108
+ });
109
+ }
110
+
111
+ // Error state
112
+ if (error) {
113
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
114
+ style: [styles.container, style],
115
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
116
+ style: styles.errorContainer,
117
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
118
+ style: styles.errorText,
119
+ children: "Error loading stack"
120
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
121
+ style: styles.errorDetail,
122
+ children: error.message
123
+ })]
124
+ })
125
+ });
126
+ }
127
+
128
+ // Empty state
129
+ if (stack.length === 0) {
130
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
131
+ style: [styles.container, style],
132
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
133
+ style: styles.emptyContainer,
134
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
135
+ style: styles.emptyText,
136
+ children: "No navigation stack"
137
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
138
+ style: styles.emptySubtext,
139
+ children: "Navigate to a route to see the stack"
140
+ })]
141
+ })
142
+ });
143
+ }
144
+
145
+ // Handlers
146
+ const handleGoBack = () => {
147
+ // Gate behind Pro
148
+ if (!isPro) {
149
+ setShowUpgradeModal(true);
150
+ return;
151
+ }
152
+ if (isAtRoot) {
153
+ _reactNative.Alert.alert("Cannot Go Back", "Already at the root of the stack");
154
+ return;
155
+ }
156
+ goBack();
157
+ };
158
+ const handlePopToTop = () => {
159
+ // Gate behind Pro
160
+ if (!isPro) {
161
+ setShowUpgradeModal(true);
162
+ return;
163
+ }
164
+ if (isAtRoot) {
165
+ _reactNative.Alert.alert("Already at Top", "Stack only has one screen");
166
+ return;
167
+ }
168
+ _reactNative.Alert.alert("Pop to Top", "This will remove all screens except the root screen.", [{
169
+ text: "Cancel",
170
+ style: "cancel"
171
+ }, {
172
+ text: "Pop to Top",
173
+ style: "destructive",
174
+ onPress: popToTop
175
+ }]);
176
+ };
177
+ const toggleExpand = index => {
178
+ setExpandedIndex(expandedIndex === index ? null : index);
179
+ };
180
+ const handleGo = () => {
181
+ // Gate behind Pro
182
+ if (!isPro) {
183
+ setShowUpgradeModal(true);
184
+ return;
185
+ }
186
+ if (!selectedRoute) return;
187
+
188
+ // If the selected route is already focused, do nothing
189
+ if (selectedRoute.isFocused) {
190
+ _reactNative.Alert.alert("Already There", "This route is already visible");
191
+ return;
192
+ }
193
+
194
+ // Navigate to the selected route
195
+ navigateToIndex(selectedRoute.index);
196
+ };
197
+ const handlePopTo = () => {
198
+ // Gate behind Pro
199
+ if (!isPro) {
200
+ setShowUpgradeModal(true);
201
+ return;
202
+ }
203
+ if (!selectedRoute) return;
204
+
205
+ // If selected route is the focused one, we can't pop to it
206
+ if (selectedRoute.isFocused) {
207
+ _reactNative.Alert.alert("Cannot Pop", "Selected route is already visible");
208
+ return;
209
+ }
210
+
211
+ // If selected route is at the top of stack, nothing to pop
212
+ if (selectedRoute.index === stackDepth - 1) {
213
+ _reactNative.Alert.alert("Cannot Pop", "No screens above selected route");
214
+ return;
215
+ }
216
+ const screensToRemove = stackDepth - 1 - selectedRoute.index;
217
+ _reactNative.Alert.alert("Pop to Route", `Remove ${screensToRemove} screen${screensToRemove !== 1 ? "s" : ""} above ${selectedRoute.pathname}?`, [{
218
+ text: "Cancel",
219
+ style: "cancel"
220
+ }, {
221
+ text: "Pop",
222
+ style: "destructive",
223
+ onPress: () => {
224
+ // Navigate to the selected route, which effectively pops everything above it
225
+ navigateToIndex(selectedRoute.index);
226
+ }
227
+ }]);
228
+ };
229
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
230
+ style: [styles.container, style],
231
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
232
+ style: styles.header,
233
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
234
+ style: [styles.iconButton, showHelp && styles.iconButtonActive],
235
+ onPress: () => setShowHelp(!showHelp),
236
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Info, {
237
+ size: 16,
238
+ color: showHelp ? _sharedUi.buoyColors.primary : _sharedUi.buoyColors.textSecondary
239
+ })
240
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InlineCopyButton, {
241
+ value: stackDataForCopy,
242
+ buttonStyle: styles.iconButton
243
+ })]
244
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
245
+ style: styles.stackScroll,
246
+ contentContainerStyle: styles.stackContent,
247
+ children: [...stack].reverse().map((item, reverseIndex) => {
248
+ const actualIndex = stack.length - 1 - reverseIndex;
249
+ const isExpanded = expandedIndex === actualIndex;
250
+ const hasParams = Object.keys(item.params).length > 0;
251
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
252
+ style: styles.stackItemWrapper,
253
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
254
+ style: [styles.stackItem, isExpanded && styles.stackItemSelected],
255
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
256
+ style: styles.stackItemHeader,
257
+ onPress: () => toggleExpand(actualIndex),
258
+ activeOpacity: 0.7,
259
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
260
+ style: styles.expandIndicator,
261
+ children: isExpanded ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronDown, {
262
+ size: 14,
263
+ color: _sharedUi.buoyColors.textSecondary
264
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChevronRight, {
265
+ size: 14,
266
+ color: _sharedUi.buoyColors.textSecondary
267
+ })
268
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
269
+ style: styles.stackItemPath,
270
+ numberOfLines: 1,
271
+ children: item.pathname
272
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InlineCopyButton, {
273
+ value: item.pathname,
274
+ buttonStyle: styles.copyRouteButton
275
+ }), item.isFocused ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
276
+ style: styles.focusedBadge,
277
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
278
+ style: styles.focusedBadgeText,
279
+ children: "VISIBLE"
280
+ })
281
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
282
+ style: styles.memoryBadge,
283
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
284
+ style: styles.memoryBadgeText,
285
+ children: "MEMORY"
286
+ })
287
+ })]
288
+ }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
289
+ style: styles.expandedContent,
290
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
291
+ style: styles.detailRow,
292
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
293
+ style: styles.detailLabel,
294
+ children: "Route Name:"
295
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
296
+ style: styles.detailValue,
297
+ children: item.name
298
+ })]
299
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
300
+ style: styles.detailRow,
301
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
302
+ style: styles.detailLabel,
303
+ children: "Position:"
304
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
305
+ style: styles.detailValue,
306
+ children: [actualIndex, " / ", stackDepth - 1]
307
+ })]
308
+ }), hasParams && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
309
+ style: styles.dataViewerContainer,
310
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_dataViewer.DataViewer, {
311
+ title: "Parameters",
312
+ data: item.params,
313
+ showTypeFilter: false
314
+ })
315
+ })]
316
+ })]
317
+ })
318
+ }, `stack-${actualIndex}-${item.key}`);
319
+ })
320
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
321
+ style: styles.actionsContainer,
322
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
323
+ style: styles.actionsRow,
324
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
325
+ style: styles.actionWrapper,
326
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
327
+ style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
328
+ onPress: handleGoBack,
329
+ disabled: isAtRoot,
330
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
331
+ style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
332
+ children: "Back"
333
+ })
334
+ }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
335
+ style: styles.helpText,
336
+ children: "Go back one screen"
337
+ })]
338
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
339
+ style: styles.actionWrapper,
340
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
341
+ style: [styles.actionButton, selectedRoute?.isFocused && styles.actionButtonDisabled],
342
+ onPress: handleGo,
343
+ disabled: selectedRoute?.isFocused,
344
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
345
+ style: [styles.actionButtonText, selectedRoute?.isFocused && styles.actionButtonTextDisabled],
346
+ children: "Go"
347
+ })
348
+ }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
349
+ style: styles.helpText,
350
+ children: "Navigate to selected route"
351
+ })]
352
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
353
+ style: styles.actionWrapper,
354
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
355
+ style: [styles.actionButton, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonDisabled],
356
+ onPress: handlePopTo,
357
+ disabled: selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1,
358
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
359
+ style: [styles.actionButtonText, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonTextDisabled],
360
+ children: "Pop To"
361
+ })
362
+ }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
363
+ style: styles.helpText,
364
+ children: "Remove screens above selected"
365
+ })]
366
+ })]
367
+ })
368
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
369
+ visible: showUpgradeModal,
370
+ onClose: () => setShowUpgradeModal(false),
371
+ featureName: "Stack Navigation"
372
+ })]
373
+ });
374
+ }
375
+
376
+ // ============================================================================
377
+ // Styles
378
+ // ============================================================================
379
+
380
+ const styles = _reactNative.StyleSheet.create({
381
+ container: {
382
+ flex: 1,
383
+ backgroundColor: _sharedUi.buoyColors.base
384
+ },
385
+ loadingContainer: {
386
+ flex: 1,
387
+ justifyContent: "center",
388
+ alignItems: "center",
389
+ padding: 32
390
+ },
391
+ loadingText: {
392
+ color: _sharedUi.buoyColors.textSecondary,
393
+ fontSize: 14,
394
+ fontFamily: "monospace"
395
+ },
396
+ errorContainer: {
397
+ flex: 1,
398
+ justifyContent: "center",
399
+ alignItems: "center",
400
+ padding: 32
401
+ },
402
+ errorText: {
403
+ color: _sharedUi.buoyColors.error,
404
+ fontSize: 16,
405
+ fontWeight: "600",
406
+ fontFamily: "monospace",
407
+ marginBottom: 8
408
+ },
409
+ errorDetail: {
410
+ color: _sharedUi.buoyColors.textSecondary,
411
+ fontSize: 12,
412
+ fontFamily: "monospace",
413
+ textAlign: "center"
414
+ },
415
+ emptyContainer: {
416
+ flex: 1,
417
+ justifyContent: "center",
418
+ alignItems: "center",
419
+ padding: 32
420
+ },
421
+ emptyText: {
422
+ color: _sharedUi.buoyColors.text,
423
+ fontSize: 16,
424
+ fontWeight: "600",
425
+ fontFamily: "monospace",
426
+ marginBottom: 8
427
+ },
428
+ emptySubtext: {
429
+ color: _sharedUi.buoyColors.textSecondary,
430
+ fontSize: 14,
431
+ fontFamily: "monospace",
432
+ textAlign: "center"
433
+ },
434
+ header: {
435
+ flexDirection: "row",
436
+ padding: 8,
437
+ gap: 8,
438
+ borderBottomWidth: 1,
439
+ borderBottomColor: _sharedUi.buoyColors.border,
440
+ alignItems: "center",
441
+ justifyContent: "flex-end"
442
+ },
443
+ iconButton: {
444
+ padding: 6,
445
+ borderRadius: 4
446
+ },
447
+ iconButtonActive: {
448
+ backgroundColor: _sharedUi.buoyColors.input
449
+ },
450
+ stackScroll: {
451
+ flex: 1
452
+ },
453
+ stackContent: {
454
+ padding: 8
455
+ },
456
+ stackItemWrapper: {
457
+ marginBottom: 6
458
+ },
459
+ stackItem: {
460
+ backgroundColor: _sharedUi.buoyColors.card,
461
+ borderRadius: 6,
462
+ borderWidth: 1,
463
+ borderColor: _sharedUi.buoyColors.border,
464
+ overflow: "hidden"
465
+ },
466
+ stackItemSelected: {
467
+ borderColor: _sharedUi.buoyColors.primary,
468
+ borderWidth: 2
469
+ },
470
+ stackItemHeader: {
471
+ flexDirection: "row",
472
+ alignItems: "center",
473
+ paddingVertical: 10,
474
+ paddingHorizontal: 12,
475
+ gap: 8
476
+ },
477
+ expandIndicator: {
478
+ width: 20,
479
+ alignItems: "center"
480
+ },
481
+ stackItemPath: {
482
+ fontSize: 13,
483
+ fontWeight: "600",
484
+ color: _sharedUi.buoyColors.text,
485
+ fontFamily: "monospace",
486
+ flex: 1
487
+ },
488
+ copyRouteButton: {
489
+ padding: 4,
490
+ marginLeft: 4
491
+ },
492
+ focusedBadge: {
493
+ backgroundColor: _sharedUi.buoyColors.success,
494
+ paddingHorizontal: 8,
495
+ paddingVertical: 3,
496
+ borderRadius: 4
497
+ },
498
+ focusedBadgeText: {
499
+ fontSize: 9,
500
+ fontWeight: "700",
501
+ color: _sharedUi.buoyColors.base,
502
+ fontFamily: "monospace"
503
+ },
504
+ memoryBadge: {
505
+ backgroundColor: _sharedUi.buoyColors.input,
506
+ paddingHorizontal: 8,
507
+ paddingVertical: 3,
508
+ borderRadius: 4
509
+ },
510
+ memoryBadgeText: {
511
+ fontSize: 9,
512
+ fontWeight: "600",
513
+ color: _sharedUi.buoyColors.textSecondary,
514
+ fontFamily: "monospace"
515
+ },
516
+ expandedContent: {
517
+ paddingHorizontal: 12,
518
+ paddingTop: 8,
519
+ paddingBottom: 12,
520
+ borderTopWidth: 1,
521
+ borderTopColor: _sharedUi.buoyColors.border
522
+ },
523
+ detailRow: {
524
+ marginBottom: 8
525
+ },
526
+ detailLabel: {
527
+ fontSize: 10,
528
+ color: _sharedUi.buoyColors.textSecondary,
529
+ fontFamily: "monospace",
530
+ marginBottom: 2
531
+ },
532
+ detailValue: {
533
+ fontSize: 12,
534
+ color: _sharedUi.buoyColors.text,
535
+ fontFamily: "monospace"
536
+ },
537
+ dataViewerContainer: {
538
+ marginTop: 4,
539
+ marginHorizontal: -12,
540
+ marginBottom: 8
541
+ },
542
+ actionsContainer: {
543
+ borderTopWidth: 1,
544
+ borderTopColor: _sharedUi.buoyColors.border,
545
+ paddingHorizontal: 8,
546
+ paddingTop: 8,
547
+ paddingBottom: 8
548
+ },
549
+ actionsRow: {
550
+ flexDirection: "row",
551
+ gap: 6,
552
+ marginBottom: 6
553
+ },
554
+ actionWrapper: {
555
+ flex: 1
556
+ },
557
+ actionButton: {
558
+ backgroundColor: _sharedUi.buoyColors.input,
559
+ paddingVertical: 8,
560
+ paddingHorizontal: 8,
561
+ borderRadius: 6,
562
+ alignItems: "center"
563
+ },
564
+ actionButtonDisabled: {
565
+ opacity: 0.5
566
+ },
567
+ actionButtonText: {
568
+ fontSize: 12,
569
+ fontWeight: "600",
570
+ color: _sharedUi.buoyColors.text,
571
+ fontFamily: "monospace"
572
+ },
573
+ actionButtonTextDisabled: {
574
+ color: _sharedUi.buoyColors.textMuted
575
+ },
576
+ helpText: {
577
+ fontSize: 9,
578
+ color: _sharedUi.buoyColors.textSecondary,
579
+ fontFamily: "monospace",
580
+ textAlign: "center",
581
+ marginTop: 4,
582
+ lineHeight: 12
583
+ }
584
+ });