@buoy-gg/route-events 3.0.2 → 4.0.1

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.
@@ -31,10 +31,14 @@ var _jsxRuntime = require("react/jsx-runtime");
31
31
  function NavigationStack({
32
32
  style,
33
33
  injectedStack,
34
- onAction
34
+ onAction,
35
+ onCopyValueChange
35
36
  }) {
36
37
  const hookResult = (0, _useNavigationStack.useNavigationStack)();
37
38
  const isInjected = injectedStack != null;
39
+ const insets = (0, _sharedUi.useSafeAreaInsets)({
40
+ minBottom: 8
41
+ });
38
42
 
39
43
  // Data source: injected (dashboard) or the local navigation container.
40
44
  const stack = isInjected ? injectedStack : hookResult.stack;
@@ -51,7 +55,6 @@ function NavigationStack({
51
55
  const goBack = () => onAction ? onAction("goBack") : hookResult.goBack();
52
56
  const popToTop = () => onAction ? onAction("popToTop") : hookResult.popToTop();
53
57
  const [expandedIndex, setExpandedIndex] = (0, _react.useState)(null);
54
- const [showHelp, setShowHelp] = (0, _react.useState)(false);
55
58
  const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
56
59
 
57
60
  // Check Pro status internally
@@ -76,6 +79,12 @@ function NavigationStack({
76
79
  return JSON.stringify(stackData, null, 2);
77
80
  }, [stack]);
78
81
 
82
+ // Report the copy payload up so the host can render the copy button in the
83
+ // shared navbar.
84
+ (0, _react.useEffect)(() => {
85
+ onCopyValueChange?.(stackDataForCopy);
86
+ }, [stackDataForCopy, onCopyValueChange]);
87
+
79
88
  // Determine which route actions should operate on
80
89
  // If a stack item is expanded, actions target that route
81
90
  // Otherwise, actions target the focused (visible) route
@@ -229,23 +238,10 @@ function NavigationStack({
229
238
  };
230
239
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
231
240
  style: [styles.container, style],
232
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
233
- style: styles.header,
234
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
235
- style: [styles.iconButton, showHelp && styles.iconButtonActive],
236
- onPress: () => setShowHelp(!showHelp),
237
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Info, {
238
- size: 16,
239
- color: showHelp ? _sharedUi.buoyColors.primary : _sharedUi.buoyColors.textSecondary
240
- })
241
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.InlineCopyButton, {
242
- value: stackDataForCopy,
243
- buttonStyle: styles.iconButton
244
- })]
245
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
241
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
246
242
  style: styles.stackScroll,
247
243
  contentContainerStyle: [styles.stackContent, {
248
- paddingBottom: showHelp ? 72 : 56
244
+ paddingBottom: styles.stackContent.padding + insets.bottom
249
245
  }],
250
246
  children: [...stack].reverse().map((item, reverseIndex) => {
251
247
  const actualIndex = stack.length - 1 - reverseIndex;
@@ -314,59 +310,56 @@ function NavigationStack({
314
310
  data: item.params,
315
311
  showTypeFilter: false
316
312
  })
313
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
314
+ style: styles.actionsRow,
315
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
316
+ style: styles.actionWrapper,
317
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
318
+ style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
319
+ onPress: handleGoBack,
320
+ disabled: isAtRoot,
321
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
322
+ style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
323
+ children: "Back"
324
+ })
325
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
326
+ style: styles.helpText,
327
+ children: "Go back one screen"
328
+ })]
329
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
330
+ style: styles.actionWrapper,
331
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
332
+ style: [styles.actionButton, item.isFocused && styles.actionButtonDisabled],
333
+ onPress: handleGo,
334
+ disabled: item.isFocused,
335
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
336
+ style: [styles.actionButtonText, item.isFocused && styles.actionButtonTextDisabled],
337
+ children: "Go"
338
+ })
339
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
340
+ style: styles.helpText,
341
+ children: "Navigate to this route"
342
+ })]
343
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
344
+ style: styles.actionWrapper,
345
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
346
+ style: [styles.actionButton, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonDisabled],
347
+ onPress: handlePopTo,
348
+ disabled: item.isFocused || actualIndex === stackDepth - 1,
349
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
350
+ style: [styles.actionButtonText, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonTextDisabled],
351
+ children: "Pop To"
352
+ })
353
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
354
+ style: styles.helpText,
355
+ children: "Remove screens above this"
356
+ })]
357
+ })]
317
358
  })]
318
359
  })]
319
360
  })
320
361
  }, `stack-${actualIndex}-${item.key}`);
321
362
  })
322
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
323
- style: styles.actionsContainer,
324
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
325
- style: styles.actionsRow,
326
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
327
- style: styles.actionWrapper,
328
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
329
- style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
330
- onPress: handleGoBack,
331
- disabled: isAtRoot,
332
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
333
- style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
334
- children: "Back"
335
- })
336
- }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
337
- style: styles.helpText,
338
- children: "Go back one screen"
339
- })]
340
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
341
- style: styles.actionWrapper,
342
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
343
- style: [styles.actionButton, selectedRoute?.isFocused && styles.actionButtonDisabled],
344
- onPress: handleGo,
345
- disabled: selectedRoute?.isFocused,
346
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
347
- style: [styles.actionButtonText, selectedRoute?.isFocused && styles.actionButtonTextDisabled],
348
- children: "Go"
349
- })
350
- }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
351
- style: styles.helpText,
352
- children: "Navigate to selected route"
353
- })]
354
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
355
- style: styles.actionWrapper,
356
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
357
- style: [styles.actionButton, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonDisabled],
358
- onPress: handlePopTo,
359
- disabled: selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1,
360
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
361
- style: [styles.actionButtonText, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonTextDisabled],
362
- children: "Pop To"
363
- })
364
- }), showHelp && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
365
- style: styles.helpText,
366
- children: "Remove screens above selected"
367
- })]
368
- })]
369
- })
370
363
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ProUpgradeModal, {
371
364
  visible: showUpgradeModal,
372
365
  onClose: () => setShowUpgradeModal(false),
@@ -433,22 +426,6 @@ const styles = _reactNative.StyleSheet.create({
433
426
  fontFamily: "monospace",
434
427
  textAlign: "center"
435
428
  },
436
- header: {
437
- flexDirection: "row",
438
- padding: 8,
439
- gap: 8,
440
- borderBottomWidth: 1,
441
- borderBottomColor: _sharedUi.buoyColors.border,
442
- alignItems: "center",
443
- justifyContent: "flex-end"
444
- },
445
- iconButton: {
446
- padding: 6,
447
- borderRadius: 4
448
- },
449
- iconButtonActive: {
450
- backgroundColor: _sharedUi.buoyColors.input
451
- },
452
429
  stackScroll: {
453
430
  flex: 1
454
431
  },
@@ -528,22 +505,10 @@ const styles = _reactNative.StyleSheet.create({
528
505
  marginHorizontal: -12,
529
506
  marginBottom: 8
530
507
  },
531
- actionsContainer: {
532
- position: "absolute",
533
- left: 0,
534
- right: 0,
535
- bottom: 0,
536
- borderTopWidth: 1,
537
- borderTopColor: _sharedUi.buoyColors.border,
538
- backgroundColor: _sharedUi.buoyColors.base,
539
- paddingHorizontal: 8,
540
- paddingTop: 8,
541
- paddingBottom: 8
542
- },
543
508
  actionsRow: {
544
509
  flexDirection: "row",
545
510
  gap: 6,
546
- marginBottom: 6
511
+ marginTop: 12
547
512
  },
548
513
  actionWrapper: {
549
514
  flex: 1
@@ -79,6 +79,9 @@ function RouteEventsModalWithTabs({
79
79
  const hasExpoRouter = (0, _sharedUi.isExpoRouterAvailable)();
80
80
  const [activeTab, setActiveTab] = (0, _react.useState)("events");
81
81
  const [showUpgradeModal, setShowUpgradeModal] = (0, _react.useState)(false);
82
+ // Serialized stack reported up by NavigationStack so the copy button can live
83
+ // in the shared navbar.
84
+ const [stackCopyValue, setStackCopyValue] = (0, _react.useState)("");
82
85
 
83
86
  // Check Pro status internally
84
87
  const isPro = (0, _license.useIsPro)();
@@ -377,7 +380,8 @@ function RouteEventsModalWithTabs({
377
380
  }
378
381
  if (activeTab === "stack") {
379
382
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_NavigationStack.NavigationStack, {
380
- style: styles.contentWrapper
383
+ style: styles.contentWrapper,
384
+ onCopyValueChange: setStackCopyValue
381
385
  });
382
386
  }
383
387
 
@@ -482,8 +486,11 @@ function RouteEventsModalWithTabs({
482
486
  activeTab: activeTab,
483
487
  onTabChange: tab => setActiveTab(tab)
484
488
  })
485
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ModalHeader.Actions, {
486
- children: activeTab === "events" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
489
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_sharedUi.ModalHeader.Actions, {
490
+ children: [activeTab === "stack" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
491
+ value: stackCopyValue,
492
+ buttonStyle: styles.iconButton
493
+ }), activeTab === "events" && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
487
494
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
488
495
  value: copyAllEventsData,
489
496
  buttonStyle: styles.iconButton
@@ -506,7 +513,7 @@ function RouteEventsModalWithTabs({
506
513
  color: _sharedUi.buoyColors.error
507
514
  })
508
515
  })]
509
- })
516
+ })]
510
517
  })]
511
518
  })
512
519
  },
@@ -25,6 +25,11 @@ var _jsxRuntime = require("react/jsx-runtime");
25
25
  // Types
26
26
  // ============================================================================
27
27
 
28
+ // On desktop (web) the toolbar has far more room, so scale the action buttons
29
+ // up for easier clicking and a less cramped header.
30
+ const IS_DESKTOP = _reactNative.Platform.OS === "web";
31
+ const ACTION_ICON_SIZE = IS_DESKTOP ? 22 : 16;
32
+
28
33
  // Synthetic route representing the app's home/index ("/"). Used by the
29
34
  // "Home" toolbar shortcut so navigation works the same as tapping any route.
30
35
  const HOME_ROUTE = {
@@ -168,7 +173,7 @@ function RoutesSitemap({
168
173
  }
169
174
  }], "plain-text");
170
175
  }, [router]);
171
- const handleNavigate = (0, _react.useCallback)(route => {
176
+ const handleNavigate = (0, _react.useCallback)((route, options) => {
172
177
  // When a navigation delegate is supplied (dashboard → device), hand off
173
178
  // entirely: the delegate owns Pro gating and param resolution.
174
179
  if (onNavigateRoute) {
@@ -176,8 +181,8 @@ function RoutesSitemap({
176
181
  return;
177
182
  }
178
183
 
179
- // Gate behind Pro
180
- if (!isPro) {
184
+ // Gate behind Pro (the Home shortcut bypasses this — it's always free)
185
+ if (!isPro && !options?.bypassPro) {
181
186
  setShowUpgradeModal(true);
182
187
  return;
183
188
  }
@@ -213,7 +218,9 @@ function RoutesSitemap({
213
218
  }
214
219
  }, [router, promptForParams, isPro, onNavigateRoute]);
215
220
  const handleGoHome = (0, _react.useCallback)(() => {
216
- handleNavigate(HOME_ROUTE);
221
+ handleNavigate(HOME_ROUTE, {
222
+ bypassPro: true
223
+ });
217
224
  }, [handleNavigate]);
218
225
  const handleManualRefresh = (0, _react.useCallback)(() => {
219
226
  if (isRefreshing) return;
@@ -261,53 +268,54 @@ function RoutesSitemap({
261
268
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
262
269
  style: styles.actionsRow,
263
270
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
264
- style: styles.actionWrapper,
271
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
265
272
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
266
- style: styles.iconButton,
273
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop],
267
274
  onPress: handleGoHome,
268
275
  accessibilityLabel: "Go to home route",
269
276
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Home, {
270
- size: 16,
277
+ size: ACTION_ICON_SIZE,
271
278
  color: _sharedUi.buoyColors.textSecondary
272
279
  })
273
280
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
274
- style: styles.actionLabel,
281
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
275
282
  children: "Home"
276
283
  })]
277
284
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
278
- style: styles.actionWrapper,
279
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ToolbarCopyButton, {
285
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
286
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.CopyButton, {
280
287
  value: copyAllData,
281
- buttonStyle: styles.actionButtonHeader
288
+ size: IS_DESKTOP ? ACTION_ICON_SIZE : 14,
289
+ buttonStyle: IS_DESKTOP ? styles.actionButtonHeaderDesktop : styles.actionButtonHeader
282
290
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
283
- style: styles.actionLabel,
291
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
284
292
  children: "Copy"
285
293
  })]
286
294
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
287
- style: styles.actionWrapper,
295
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
288
296
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
289
- style: [styles.iconButton, isRefreshing && styles.refreshButtonDisabled],
297
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop, isRefreshing && styles.refreshButtonDisabled],
290
298
  onPress: handleManualRefresh,
291
299
  disabled: isRefreshing,
292
300
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.RefreshCw, {
293
- size: 16,
301
+ size: ACTION_ICON_SIZE,
294
302
  color: isRefreshing ? _sharedUi.buoyColors.textMuted : _sharedUi.buoyColors.textSecondary
295
303
  })
296
304
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
297
- style: styles.actionLabel,
305
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
298
306
  children: "Refresh"
299
307
  })]
300
308
  }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
301
- style: styles.actionWrapper,
309
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
302
310
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
303
- style: styles.iconButton,
311
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop],
304
312
  onPress: () => setIsSearching(true),
305
313
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.Search, {
306
- size: 16,
314
+ size: ACTION_ICON_SIZE,
307
315
  color: _sharedUi.buoyColors.textSecondary
308
316
  })
309
317
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
310
- style: styles.actionLabel,
318
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
311
319
  children: "Search"
312
320
  })]
313
321
  })]
@@ -453,18 +461,18 @@ function RouteGroupView({
453
461
  children: group.routes.length
454
462
  })
455
463
  })]
456
- }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
464
+ }), group.description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
465
+ style: styles.groupDescription,
466
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
467
+ style: styles.groupDescriptionText,
468
+ children: group.description
469
+ })
470
+ }), isExpanded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
457
471
  style: styles.routesList,
458
- children: [group.description && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
459
- style: styles.groupDescription,
460
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
461
- style: styles.groupDescriptionText,
462
- children: group.description
463
- })
464
- }), group.routes.map((route, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteItemView, {
472
+ children: group.routes.map((route, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteItemView, {
465
473
  route: route,
466
474
  onNavigate: onNavigate
467
- }, `${route.path}-${index}`))]
475
+ }, `${route.path}-${index}`))
468
476
  })]
469
477
  });
470
478
  }
@@ -475,7 +483,6 @@ function RouteItemView({
475
483
  }) {
476
484
  const [isExpanded, setIsExpanded] = (0, _react.useState)(false);
477
485
  const hasChildren = route.children.length > 0;
478
- const hasParams = route.params.length > 0;
479
486
  const typeColor = getRouteTypeColor(route.type);
480
487
  const canNavigate = route.type !== "layout" && route.type !== "group";
481
488
 
@@ -554,15 +561,6 @@ function RouteItemView({
554
561
  })
555
562
  })]
556
563
  })]
557
- }), showDetails && hasParams && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
558
- style: styles.paramsRow,
559
- children: route.params.map(param => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
560
- style: styles.paramTag,
561
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
562
- style: styles.paramText,
563
- children: param
564
- })
565
- }, param))
566
564
  }), showDetails && hasChildren && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
567
565
  style: styles.childrenContainer,
568
566
  children: route.children.map((child, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(RouteItemView, {
@@ -682,17 +680,31 @@ const styles = _reactNative.StyleSheet.create({
682
680
  gap: 4,
683
681
  minWidth: 48
684
682
  },
683
+ actionWrapperDesktop: {
684
+ gap: 8,
685
+ minWidth: 72
686
+ },
685
687
  actionButtonHeader: {
686
688
  padding: 6,
687
689
  borderRadius: 4,
688
690
  backgroundColor: _sharedUi.buoyColors.input
689
691
  },
692
+ actionButtonHeaderDesktop: {
693
+ padding: 12,
694
+ borderRadius: 8,
695
+ backgroundColor: _sharedUi.buoyColors.input
696
+ },
690
697
  iconButton: {
691
698
  padding: 6,
692
699
  borderRadius: 4,
693
700
  alignItems: "center",
694
701
  justifyContent: "center"
695
702
  },
703
+ iconButtonDesktop: {
704
+ padding: 12,
705
+ borderRadius: 8,
706
+ backgroundColor: _sharedUi.buoyColors.input
707
+ },
696
708
  actionLabel: {
697
709
  fontSize: 8,
698
710
  color: _sharedUi.buoyColors.textMuted,
@@ -700,6 +712,9 @@ const styles = _reactNative.StyleSheet.create({
700
712
  textTransform: "uppercase",
701
713
  letterSpacing: 0.5
702
714
  },
715
+ actionLabelDesktop: {
716
+ fontSize: 12
717
+ },
703
718
  refreshButtonDisabled: {
704
719
  opacity: 0.5
705
720
  },
@@ -944,28 +959,6 @@ const styles = _reactNative.StyleSheet.create({
944
959
  fontFamily: "monospace",
945
960
  fontWeight: "600"
946
961
  },
947
- paramsRow: {
948
- flexDirection: "row",
949
- flexWrap: "wrap",
950
- gap: 6,
951
- paddingHorizontal: 10,
952
- paddingTop: 2,
953
- paddingBottom: 8
954
- },
955
- paramTag: {
956
- backgroundColor: "#F59E0B15",
957
- borderColor: "#F59E0B40",
958
- borderWidth: 1,
959
- borderRadius: 4,
960
- paddingHorizontal: 6,
961
- paddingVertical: 2
962
- },
963
- paramText: {
964
- fontSize: 10,
965
- color: "#F59E0B",
966
- fontFamily: "monospace",
967
- fontWeight: "600"
968
- },
969
962
  childrenContainer: {
970
963
  borderLeftWidth: 2,
971
964
  borderLeftColor: _sharedUi.buoyColors.border,
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { useState, useMemo, useEffect } from "react";
11
11
  import { View, Text, ScrollView, TouchableOpacity, StyleSheet, Alert } from "react-native";
12
- import { ChevronDown, ChevronRight, Info, InlineCopyButton, ProUpgradeModal, buoyColors } from "@buoy-gg/shared-ui";
12
+ import { ChevronDown, ChevronRight, InlineCopyButton, ProUpgradeModal, buoyColors, useSafeAreaInsets } from "@buoy-gg/shared-ui";
13
13
  import { useIsPro } from "@buoy-gg/license";
14
14
  import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
15
15
  import { useNavigationStack } from "../useNavigationStack";
@@ -27,10 +27,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
27
27
  export function NavigationStack({
28
28
  style,
29
29
  injectedStack,
30
- onAction
30
+ onAction,
31
+ onCopyValueChange
31
32
  }) {
32
33
  const hookResult = useNavigationStack();
33
34
  const isInjected = injectedStack != null;
35
+ const insets = useSafeAreaInsets({
36
+ minBottom: 8
37
+ });
34
38
 
35
39
  // Data source: injected (dashboard) or the local navigation container.
36
40
  const stack = isInjected ? injectedStack : hookResult.stack;
@@ -47,7 +51,6 @@ export function NavigationStack({
47
51
  const goBack = () => onAction ? onAction("goBack") : hookResult.goBack();
48
52
  const popToTop = () => onAction ? onAction("popToTop") : hookResult.popToTop();
49
53
  const [expandedIndex, setExpandedIndex] = useState(null);
50
- const [showHelp, setShowHelp] = useState(false);
51
54
  const [showUpgradeModal, setShowUpgradeModal] = useState(false);
52
55
 
53
56
  // Check Pro status internally
@@ -72,6 +75,12 @@ export function NavigationStack({
72
75
  return JSON.stringify(stackData, null, 2);
73
76
  }, [stack]);
74
77
 
78
+ // Report the copy payload up so the host can render the copy button in the
79
+ // shared navbar.
80
+ useEffect(() => {
81
+ onCopyValueChange?.(stackDataForCopy);
82
+ }, [stackDataForCopy, onCopyValueChange]);
83
+
75
84
  // Determine which route actions should operate on
76
85
  // If a stack item is expanded, actions target that route
77
86
  // Otherwise, actions target the focused (visible) route
@@ -225,23 +234,10 @@ export function NavigationStack({
225
234
  };
226
235
  return /*#__PURE__*/_jsxs(View, {
227
236
  style: [styles.container, style],
228
- children: [/*#__PURE__*/_jsxs(View, {
229
- style: styles.header,
230
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
231
- style: [styles.iconButton, showHelp && styles.iconButtonActive],
232
- onPress: () => setShowHelp(!showHelp),
233
- children: /*#__PURE__*/_jsx(Info, {
234
- size: 16,
235
- color: showHelp ? buoyColors.primary : buoyColors.textSecondary
236
- })
237
- }), /*#__PURE__*/_jsx(InlineCopyButton, {
238
- value: stackDataForCopy,
239
- buttonStyle: styles.iconButton
240
- })]
241
- }), /*#__PURE__*/_jsx(ScrollView, {
237
+ children: [/*#__PURE__*/_jsx(ScrollView, {
242
238
  style: styles.stackScroll,
243
239
  contentContainerStyle: [styles.stackContent, {
244
- paddingBottom: showHelp ? 72 : 56
240
+ paddingBottom: styles.stackContent.padding + insets.bottom
245
241
  }],
246
242
  children: [...stack].reverse().map((item, reverseIndex) => {
247
243
  const actualIndex = stack.length - 1 - reverseIndex;
@@ -310,59 +306,56 @@ export function NavigationStack({
310
306
  data: item.params,
311
307
  showTypeFilter: false
312
308
  })
309
+ }), /*#__PURE__*/_jsxs(View, {
310
+ style: styles.actionsRow,
311
+ children: [/*#__PURE__*/_jsxs(View, {
312
+ style: styles.actionWrapper,
313
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
314
+ style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
315
+ onPress: handleGoBack,
316
+ disabled: isAtRoot,
317
+ children: /*#__PURE__*/_jsx(Text, {
318
+ style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
319
+ children: "Back"
320
+ })
321
+ }), /*#__PURE__*/_jsx(Text, {
322
+ style: styles.helpText,
323
+ children: "Go back one screen"
324
+ })]
325
+ }), /*#__PURE__*/_jsxs(View, {
326
+ style: styles.actionWrapper,
327
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
328
+ style: [styles.actionButton, item.isFocused && styles.actionButtonDisabled],
329
+ onPress: handleGo,
330
+ disabled: item.isFocused,
331
+ children: /*#__PURE__*/_jsx(Text, {
332
+ style: [styles.actionButtonText, item.isFocused && styles.actionButtonTextDisabled],
333
+ children: "Go"
334
+ })
335
+ }), /*#__PURE__*/_jsx(Text, {
336
+ style: styles.helpText,
337
+ children: "Navigate to this route"
338
+ })]
339
+ }), /*#__PURE__*/_jsxs(View, {
340
+ style: styles.actionWrapper,
341
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
342
+ style: [styles.actionButton, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonDisabled],
343
+ onPress: handlePopTo,
344
+ disabled: item.isFocused || actualIndex === stackDepth - 1,
345
+ children: /*#__PURE__*/_jsx(Text, {
346
+ style: [styles.actionButtonText, (item.isFocused || actualIndex === stackDepth - 1) && styles.actionButtonTextDisabled],
347
+ children: "Pop To"
348
+ })
349
+ }), /*#__PURE__*/_jsx(Text, {
350
+ style: styles.helpText,
351
+ children: "Remove screens above this"
352
+ })]
353
+ })]
313
354
  })]
314
355
  })]
315
356
  })
316
357
  }, `stack-${actualIndex}-${item.key}`);
317
358
  })
318
- }), /*#__PURE__*/_jsx(View, {
319
- style: styles.actionsContainer,
320
- children: /*#__PURE__*/_jsxs(View, {
321
- style: styles.actionsRow,
322
- children: [/*#__PURE__*/_jsxs(View, {
323
- style: styles.actionWrapper,
324
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
325
- style: [styles.actionButton, isAtRoot && styles.actionButtonDisabled],
326
- onPress: handleGoBack,
327
- disabled: isAtRoot,
328
- children: /*#__PURE__*/_jsx(Text, {
329
- style: [styles.actionButtonText, isAtRoot && styles.actionButtonTextDisabled],
330
- children: "Back"
331
- })
332
- }), showHelp && /*#__PURE__*/_jsx(Text, {
333
- style: styles.helpText,
334
- children: "Go back one screen"
335
- })]
336
- }), /*#__PURE__*/_jsxs(View, {
337
- style: styles.actionWrapper,
338
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
339
- style: [styles.actionButton, selectedRoute?.isFocused && styles.actionButtonDisabled],
340
- onPress: handleGo,
341
- disabled: selectedRoute?.isFocused,
342
- children: /*#__PURE__*/_jsx(Text, {
343
- style: [styles.actionButtonText, selectedRoute?.isFocused && styles.actionButtonTextDisabled],
344
- children: "Go"
345
- })
346
- }), showHelp && /*#__PURE__*/_jsx(Text, {
347
- style: styles.helpText,
348
- children: "Navigate to selected route"
349
- })]
350
- }), /*#__PURE__*/_jsxs(View, {
351
- style: styles.actionWrapper,
352
- children: [/*#__PURE__*/_jsx(TouchableOpacity, {
353
- style: [styles.actionButton, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonDisabled],
354
- onPress: handlePopTo,
355
- disabled: selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1,
356
- children: /*#__PURE__*/_jsx(Text, {
357
- style: [styles.actionButtonText, (selectedRoute?.isFocused || selectedRoute?.index === stackDepth - 1) && styles.actionButtonTextDisabled],
358
- children: "Pop To"
359
- })
360
- }), showHelp && /*#__PURE__*/_jsx(Text, {
361
- style: styles.helpText,
362
- children: "Remove screens above selected"
363
- })]
364
- })]
365
- })
366
359
  }), /*#__PURE__*/_jsx(ProUpgradeModal, {
367
360
  visible: showUpgradeModal,
368
361
  onClose: () => setShowUpgradeModal(false),
@@ -429,22 +422,6 @@ const styles = StyleSheet.create({
429
422
  fontFamily: "monospace",
430
423
  textAlign: "center"
431
424
  },
432
- header: {
433
- flexDirection: "row",
434
- padding: 8,
435
- gap: 8,
436
- borderBottomWidth: 1,
437
- borderBottomColor: buoyColors.border,
438
- alignItems: "center",
439
- justifyContent: "flex-end"
440
- },
441
- iconButton: {
442
- padding: 6,
443
- borderRadius: 4
444
- },
445
- iconButtonActive: {
446
- backgroundColor: buoyColors.input
447
- },
448
425
  stackScroll: {
449
426
  flex: 1
450
427
  },
@@ -524,22 +501,10 @@ const styles = StyleSheet.create({
524
501
  marginHorizontal: -12,
525
502
  marginBottom: 8
526
503
  },
527
- actionsContainer: {
528
- position: "absolute",
529
- left: 0,
530
- right: 0,
531
- bottom: 0,
532
- borderTopWidth: 1,
533
- borderTopColor: buoyColors.border,
534
- backgroundColor: buoyColors.base,
535
- paddingHorizontal: 8,
536
- paddingTop: 8,
537
- paddingBottom: 8
538
- },
539
504
  actionsRow: {
540
505
  flexDirection: "row",
541
506
  gap: 6,
542
- marginBottom: 6
507
+ marginTop: 12
543
508
  },
544
509
  actionWrapper: {
545
510
  flex: 1
@@ -77,6 +77,9 @@ export function RouteEventsModalWithTabs({
77
77
  const hasExpoRouter = isExpoRouterAvailable();
78
78
  const [activeTab, setActiveTab] = useState("events");
79
79
  const [showUpgradeModal, setShowUpgradeModal] = useState(false);
80
+ // Serialized stack reported up by NavigationStack so the copy button can live
81
+ // in the shared navbar.
82
+ const [stackCopyValue, setStackCopyValue] = useState("");
80
83
 
81
84
  // Check Pro status internally
82
85
  const isPro = useIsPro();
@@ -375,7 +378,8 @@ export function RouteEventsModalWithTabs({
375
378
  }
376
379
  if (activeTab === "stack") {
377
380
  return /*#__PURE__*/_jsx(NavigationStack, {
378
- style: styles.contentWrapper
381
+ style: styles.contentWrapper,
382
+ onCopyValueChange: setStackCopyValue
379
383
  });
380
384
  }
381
385
 
@@ -480,8 +484,11 @@ export function RouteEventsModalWithTabs({
480
484
  activeTab: activeTab,
481
485
  onTabChange: tab => setActiveTab(tab)
482
486
  })
483
- }), /*#__PURE__*/_jsx(ModalHeader.Actions, {
484
- children: activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
487
+ }), /*#__PURE__*/_jsxs(ModalHeader.Actions, {
488
+ children: [activeTab === "stack" && /*#__PURE__*/_jsx(ToolbarCopyButton, {
489
+ value: stackCopyValue,
490
+ buttonStyle: styles.iconButton
491
+ }), activeTab === "events" && /*#__PURE__*/_jsxs(_Fragment, {
485
492
  children: [/*#__PURE__*/_jsx(ToolbarCopyButton, {
486
493
  value: copyAllEventsData,
487
494
  buttonStyle: styles.iconButton
@@ -504,7 +511,7 @@ export function RouteEventsModalWithTabs({
504
511
  color: buoyColors.error
505
512
  })
506
513
  })]
507
- })
514
+ })]
508
515
  })]
509
516
  })
510
517
  },
@@ -12,9 +12,9 @@
12
12
  */
13
13
 
14
14
  import { useState, useCallback, useMemo, useEffect } from "react";
15
- import { View, Text, TextInput, TouchableOpacity, ScrollView, StyleSheet, Alert } from "react-native";
15
+ import { View, Text, TextInput, TouchableOpacity, ScrollView, StyleSheet, Alert, Platform } from "react-native";
16
16
  import { useSafeRouter } from "@buoy-gg/shared-ui";
17
- import { Search, ChevronDown, ChevronRight, Home, InlineCopyButton, ToolbarCopyButton, RefreshCw, formatRelativeTime, ProUpgradeModal, buoyColors } from "@buoy-gg/shared-ui";
17
+ import { Search, ChevronDown, ChevronRight, Home, InlineCopyButton, CopyButton, RefreshCw, formatRelativeTime, ProUpgradeModal, buoyColors } from "@buoy-gg/shared-ui";
18
18
  import { useIsPro } from "@buoy-gg/license";
19
19
  import { useRouteSitemap } from "../useRouteSitemap";
20
20
 
@@ -22,6 +22,11 @@ import { useRouteSitemap } from "../useRouteSitemap";
22
22
  // Types
23
23
  // ============================================================================
24
24
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
25
+ // On desktop (web) the toolbar has far more room, so scale the action buttons
26
+ // up for easier clicking and a less cramped header.
27
+ const IS_DESKTOP = Platform.OS === "web";
28
+ const ACTION_ICON_SIZE = IS_DESKTOP ? 22 : 16;
29
+
25
30
  // Synthetic route representing the app's home/index ("/"). Used by the
26
31
  // "Home" toolbar shortcut so navigation works the same as tapping any route.
27
32
  const HOME_ROUTE = {
@@ -165,7 +170,7 @@ export function RoutesSitemap({
165
170
  }
166
171
  }], "plain-text");
167
172
  }, [router]);
168
- const handleNavigate = useCallback(route => {
173
+ const handleNavigate = useCallback((route, options) => {
169
174
  // When a navigation delegate is supplied (dashboard → device), hand off
170
175
  // entirely: the delegate owns Pro gating and param resolution.
171
176
  if (onNavigateRoute) {
@@ -173,8 +178,8 @@ export function RoutesSitemap({
173
178
  return;
174
179
  }
175
180
 
176
- // Gate behind Pro
177
- if (!isPro) {
181
+ // Gate behind Pro (the Home shortcut bypasses this — it's always free)
182
+ if (!isPro && !options?.bypassPro) {
178
183
  setShowUpgradeModal(true);
179
184
  return;
180
185
  }
@@ -210,7 +215,9 @@ export function RoutesSitemap({
210
215
  }
211
216
  }, [router, promptForParams, isPro, onNavigateRoute]);
212
217
  const handleGoHome = useCallback(() => {
213
- handleNavigate(HOME_ROUTE);
218
+ handleNavigate(HOME_ROUTE, {
219
+ bypassPro: true
220
+ });
214
221
  }, [handleNavigate]);
215
222
  const handleManualRefresh = useCallback(() => {
216
223
  if (isRefreshing) return;
@@ -258,53 +265,54 @@ export function RoutesSitemap({
258
265
  children: [/*#__PURE__*/_jsxs(View, {
259
266
  style: styles.actionsRow,
260
267
  children: [/*#__PURE__*/_jsxs(View, {
261
- style: styles.actionWrapper,
268
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
262
269
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
263
- style: styles.iconButton,
270
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop],
264
271
  onPress: handleGoHome,
265
272
  accessibilityLabel: "Go to home route",
266
273
  children: /*#__PURE__*/_jsx(Home, {
267
- size: 16,
274
+ size: ACTION_ICON_SIZE,
268
275
  color: buoyColors.textSecondary
269
276
  })
270
277
  }), /*#__PURE__*/_jsx(Text, {
271
- style: styles.actionLabel,
278
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
272
279
  children: "Home"
273
280
  })]
274
281
  }), /*#__PURE__*/_jsxs(View, {
275
- style: styles.actionWrapper,
276
- children: [/*#__PURE__*/_jsx(ToolbarCopyButton, {
282
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
283
+ children: [/*#__PURE__*/_jsx(CopyButton, {
277
284
  value: copyAllData,
278
- buttonStyle: styles.actionButtonHeader
285
+ size: IS_DESKTOP ? ACTION_ICON_SIZE : 14,
286
+ buttonStyle: IS_DESKTOP ? styles.actionButtonHeaderDesktop : styles.actionButtonHeader
279
287
  }), /*#__PURE__*/_jsx(Text, {
280
- style: styles.actionLabel,
288
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
281
289
  children: "Copy"
282
290
  })]
283
291
  }), /*#__PURE__*/_jsxs(View, {
284
- style: styles.actionWrapper,
292
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
285
293
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
286
- style: [styles.iconButton, isRefreshing && styles.refreshButtonDisabled],
294
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop, isRefreshing && styles.refreshButtonDisabled],
287
295
  onPress: handleManualRefresh,
288
296
  disabled: isRefreshing,
289
297
  children: /*#__PURE__*/_jsx(RefreshCw, {
290
- size: 16,
298
+ size: ACTION_ICON_SIZE,
291
299
  color: isRefreshing ? buoyColors.textMuted : buoyColors.textSecondary
292
300
  })
293
301
  }), /*#__PURE__*/_jsx(Text, {
294
- style: styles.actionLabel,
302
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
295
303
  children: "Refresh"
296
304
  })]
297
305
  }), /*#__PURE__*/_jsxs(View, {
298
- style: styles.actionWrapper,
306
+ style: [styles.actionWrapper, IS_DESKTOP && styles.actionWrapperDesktop],
299
307
  children: [/*#__PURE__*/_jsx(TouchableOpacity, {
300
- style: styles.iconButton,
308
+ style: [styles.iconButton, IS_DESKTOP && styles.iconButtonDesktop],
301
309
  onPress: () => setIsSearching(true),
302
310
  children: /*#__PURE__*/_jsx(Search, {
303
- size: 16,
311
+ size: ACTION_ICON_SIZE,
304
312
  color: buoyColors.textSecondary
305
313
  })
306
314
  }), /*#__PURE__*/_jsx(Text, {
307
- style: styles.actionLabel,
315
+ style: [styles.actionLabel, IS_DESKTOP && styles.actionLabelDesktop],
308
316
  children: "Search"
309
317
  })]
310
318
  })]
@@ -450,18 +458,18 @@ function RouteGroupView({
450
458
  children: group.routes.length
451
459
  })
452
460
  })]
453
- }), isExpanded && /*#__PURE__*/_jsxs(View, {
461
+ }), group.description && /*#__PURE__*/_jsx(View, {
462
+ style: styles.groupDescription,
463
+ children: /*#__PURE__*/_jsx(Text, {
464
+ style: styles.groupDescriptionText,
465
+ children: group.description
466
+ })
467
+ }), isExpanded && /*#__PURE__*/_jsx(View, {
454
468
  style: styles.routesList,
455
- children: [group.description && /*#__PURE__*/_jsx(View, {
456
- style: styles.groupDescription,
457
- children: /*#__PURE__*/_jsx(Text, {
458
- style: styles.groupDescriptionText,
459
- children: group.description
460
- })
461
- }), group.routes.map((route, index) => /*#__PURE__*/_jsx(RouteItemView, {
469
+ children: group.routes.map((route, index) => /*#__PURE__*/_jsx(RouteItemView, {
462
470
  route: route,
463
471
  onNavigate: onNavigate
464
- }, `${route.path}-${index}`))]
472
+ }, `${route.path}-${index}`))
465
473
  })]
466
474
  });
467
475
  }
@@ -472,7 +480,6 @@ function RouteItemView({
472
480
  }) {
473
481
  const [isExpanded, setIsExpanded] = useState(false);
474
482
  const hasChildren = route.children.length > 0;
475
- const hasParams = route.params.length > 0;
476
483
  const typeColor = getRouteTypeColor(route.type);
477
484
  const canNavigate = route.type !== "layout" && route.type !== "group";
478
485
 
@@ -551,15 +558,6 @@ function RouteItemView({
551
558
  })
552
559
  })]
553
560
  })]
554
- }), showDetails && hasParams && /*#__PURE__*/_jsx(View, {
555
- style: styles.paramsRow,
556
- children: route.params.map(param => /*#__PURE__*/_jsx(View, {
557
- style: styles.paramTag,
558
- children: /*#__PURE__*/_jsx(Text, {
559
- style: styles.paramText,
560
- children: param
561
- })
562
- }, param))
563
561
  }), showDetails && hasChildren && /*#__PURE__*/_jsx(View, {
564
562
  style: styles.childrenContainer,
565
563
  children: route.children.map((child, index) => /*#__PURE__*/_jsx(RouteItemView, {
@@ -679,17 +677,31 @@ const styles = StyleSheet.create({
679
677
  gap: 4,
680
678
  minWidth: 48
681
679
  },
680
+ actionWrapperDesktop: {
681
+ gap: 8,
682
+ minWidth: 72
683
+ },
682
684
  actionButtonHeader: {
683
685
  padding: 6,
684
686
  borderRadius: 4,
685
687
  backgroundColor: buoyColors.input
686
688
  },
689
+ actionButtonHeaderDesktop: {
690
+ padding: 12,
691
+ borderRadius: 8,
692
+ backgroundColor: buoyColors.input
693
+ },
687
694
  iconButton: {
688
695
  padding: 6,
689
696
  borderRadius: 4,
690
697
  alignItems: "center",
691
698
  justifyContent: "center"
692
699
  },
700
+ iconButtonDesktop: {
701
+ padding: 12,
702
+ borderRadius: 8,
703
+ backgroundColor: buoyColors.input
704
+ },
693
705
  actionLabel: {
694
706
  fontSize: 8,
695
707
  color: buoyColors.textMuted,
@@ -697,6 +709,9 @@ const styles = StyleSheet.create({
697
709
  textTransform: "uppercase",
698
710
  letterSpacing: 0.5
699
711
  },
712
+ actionLabelDesktop: {
713
+ fontSize: 12
714
+ },
700
715
  refreshButtonDisabled: {
701
716
  opacity: 0.5
702
717
  },
@@ -941,28 +956,6 @@ const styles = StyleSheet.create({
941
956
  fontFamily: "monospace",
942
957
  fontWeight: "600"
943
958
  },
944
- paramsRow: {
945
- flexDirection: "row",
946
- flexWrap: "wrap",
947
- gap: 6,
948
- paddingHorizontal: 10,
949
- paddingTop: 2,
950
- paddingBottom: 8
951
- },
952
- paramTag: {
953
- backgroundColor: "#F59E0B15",
954
- borderColor: "#F59E0B40",
955
- borderWidth: 1,
956
- borderRadius: 4,
957
- paddingHorizontal: 6,
958
- paddingVertical: 2
959
- },
960
- paramText: {
961
- fontSize: 10,
962
- color: "#F59E0B",
963
- fontFamily: "monospace",
964
- fontWeight: "600"
965
- },
966
959
  childrenContainer: {
967
960
  borderLeftWidth: 2,
968
961
  borderLeftColor: buoyColors.border,
@@ -23,6 +23,12 @@ export interface NavigationStackProps {
23
23
  onAction?: (action: NavigationStackActionType, payload?: {
24
24
  index?: number;
25
25
  }) => void;
26
+ /**
27
+ * Reports the serialized stack (used for the "copy" toolbar action) up to the
28
+ * host so the copy button can live in the shared navbar instead of inside
29
+ * this component. Called whenever the stack changes.
30
+ */
31
+ onCopyValueChange?: (value: string) => void;
26
32
  }
27
- export declare function NavigationStack({ style, injectedStack, onAction, }: NavigationStackProps): import("react").JSX.Element;
33
+ export declare function NavigationStack({ style, injectedStack, onAction, onCopyValueChange, }: NavigationStackProps): import("react").JSX.Element;
28
34
  //# sourceMappingURL=NavigationStack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationStack.d.ts","sourceRoot":"","sources":["../../../src/components/NavigationStack.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAM/B,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,YAAY,GACZ,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,GAAG,CAAC;IAEZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,yBAAyB,EACjC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KACzB,IAAI,CAAC;CACX;AAMD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,QAAQ,GACT,EAAE,oBAAoB,+BAgctB"}
1
+ {"version":3,"file":"NavigationStack.d.ts","sourceRoot":"","sources":["../../../src/components/NavigationStack.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,uBAAuB,CAAC;AAM/B,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GACjC,iBAAiB,GACjB,YAAY,GACZ,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,GAAG,CAAC;IAEZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CACT,MAAM,EAAE,yBAAyB,EACjC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KACzB,IAAI,CAAC;IAEV;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAMD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,QAAQ,EACR,iBAAiB,GAClB,EAAE,oBAAoB,+BAgbtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"RouteEventsModalWithTabs.d.ts","sourceRoot":"","sources":["../../../src/components/RouteEventsModalWithTabs.tsx"],"names":[],"mappings":"AAqDA,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AA8CD,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,GACpC,EAAE,6BAA6B,sCAgiB/B"}
1
+ {"version":3,"file":"RouteEventsModalWithTabs.d.ts","sourceRoot":"","sources":["../../../src/components/RouteEventsModalWithTabs.tsx"],"names":[],"mappings":"AAqDA,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AA8CD,wBAAgB,wBAAwB,CAAC,EACvC,OAAO,EACP,OAAO,EACP,MAAM,EACN,UAAU,EACV,2BAAmC,GACpC,EAAE,6BAA6B,sCA8iB/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"RoutesSitemap.d.ts","sourceRoot":"","sources":["../../../src/components/RoutesSitemap.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA2BH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAM5D,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,GAAG,CAAC;IAEZ;;;;OAIG;IACH,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAE7B,oDAAoD;IACpD,cAAc,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC;IAExC,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC9C;AAiCD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,eAAe,GAChB,EAAE,kBAAkB,+BAwYpB"}
1
+ {"version":3,"file":"RoutesSitemap.d.ts","sourceRoot":"","sources":["../../../src/components/RoutesSitemap.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA4BH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAM5D,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,GAAG,CAAC;IAEZ;;;;OAIG;IACH,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAE7B,oDAAoD;IACpD,cAAc,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC;IAExC,8DAA8D;IAC9D,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC9C;AAsCD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,eAAe,GAChB,EAAE,kBAAkB,+BA8YpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@buoy-gg/route-events",
3
- "version": "3.0.2",
3
+ "version": "4.0.1",
4
4
  "description": "route-events package",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -26,12 +26,12 @@
26
26
  ],
27
27
  "sideEffects": false,
28
28
  "dependencies": {
29
- "@buoy-gg/floating-tools-core": "3.0.2",
30
- "@buoy-gg/shared-ui": "3.0.2"
29
+ "@buoy-gg/floating-tools-core": "4.0.1",
30
+ "@buoy-gg/shared-ui": "4.0.1"
31
31
  },
32
32
  "peerDependencies": {
33
- "@buoy-gg/license": "3.0.2",
34
- "@react-native-async-storage/async-storage": "*",
33
+ "@buoy-gg/license": "4.0.1",
34
+ "@react-native-async-storage/async-storage": "^2.0.0 || ^3.1.0",
35
35
  "@react-navigation/native": "*",
36
36
  "expo-router": "*",
37
37
  "react": "*",
@@ -49,7 +49,7 @@
49
49
  "@types/react-native": "^0.73.0",
50
50
  "expo-router": "~5.0.7",
51
51
  "typescript": "~5.8.3",
52
- "@buoy-gg/license": "3.0.2"
52
+ "@buoy-gg/license": "4.0.1"
53
53
  },
54
54
  "react-native-builder-bob": {
55
55
  "source": "src",