@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,487 @@
1
+ "use strict";
2
+
3
+ import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from "react-native";
4
+ import { useEffect, useState, useCallback, useRef } from "react";
5
+ import { formatRelativeTime, devToolsStorageKeys, ChevronLeft, ChevronRight, AlertCircle, Navigation as NavigationIcon, GitBranch, safeGetItem, safeSetItem, buoyColors } from "@buoy-gg/shared-ui";
6
+ import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
7
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
8
+ export function RouteEventDetailContent({
9
+ conversation,
10
+ selectedEventIndex = 0,
11
+ onEventIndexChange = () => {},
12
+ disableInternalFooter = false
13
+ }) {
14
+ // Internal view state
15
+ const [internalActiveView, setInternalActiveView] = useState("current");
16
+
17
+ // Track if preferences have been loaded
18
+ const hasLoadedPreferences = useRef(false);
19
+
20
+ // Load saved preferences on mount
21
+ useEffect(() => {
22
+ if (hasLoadedPreferences.current) return;
23
+ const loadPreferences = async () => {
24
+ try {
25
+ // Load detail view preference (current/diff)
26
+ const savedDetailView = await safeGetItem(devToolsStorageKeys.routeEvents.detailView());
27
+ if (savedDetailView === "current" || savedDetailView === "diff") {
28
+ setInternalActiveView(savedDetailView);
29
+ }
30
+ hasLoadedPreferences.current = true;
31
+ } catch (error) {
32
+ // Failed to load view preferences
33
+ }
34
+ };
35
+ loadPreferences();
36
+ }, []);
37
+
38
+ // Save detail view preference when changed
39
+ const handleViewChange = useCallback(async view => {
40
+ setInternalActiveView(view);
41
+ try {
42
+ await safeSetItem(devToolsStorageKeys.routeEvents.detailView(), view);
43
+ } catch (error) {
44
+ // Failed to save detail view preference
45
+ }
46
+ }, []);
47
+ const renderRouteContent = (event, label) => {
48
+ return /*#__PURE__*/_jsxs(View, {
49
+ style: styles.valueContent,
50
+ children: [/*#__PURE__*/_jsx(View, {
51
+ style: styles.valueHeader,
52
+ children: /*#__PURE__*/_jsx(Text, {
53
+ style: styles.valueLabel,
54
+ children: label
55
+ })
56
+ }), /*#__PURE__*/_jsx(View, {
57
+ style: styles.valueBox,
58
+ children: /*#__PURE__*/_jsx(DataViewer, {
59
+ title: "",
60
+ data: {
61
+ pathname: event.pathname,
62
+ segments: event.segments,
63
+ params: event.params,
64
+ timestamp: new Date(event.timestamp).toISOString()
65
+ },
66
+ showTypeFilter: false
67
+ })
68
+ })]
69
+ });
70
+ };
71
+
72
+ // Get all events sorted by time
73
+ const navigationItems = conversation.events.sort((a, b) => a.timestamp - b.timestamp);
74
+ const totalEvents = navigationItems.length;
75
+
76
+ // Precise time HH:MM:SS.mmm
77
+ const formatTimeWithMs = useCallback(timestamp => {
78
+ const date = new Date(timestamp);
79
+ const h = String(date.getHours()).padStart(2, "0");
80
+ const m = String(date.getMinutes()).padStart(2, "0");
81
+ const s = String(date.getSeconds()).padStart(2, "0");
82
+ const ms = String(date.getMilliseconds()).padStart(3, "0");
83
+ return `${h}:${m}:${s}.${ms}`;
84
+ }, []);
85
+
86
+ // Render current value tab
87
+ const renderCurrentValue = () => {
88
+ const selectedEvent = navigationItems[selectedEventIndex];
89
+ const eventToShow = selectedEvent ?? conversation.lastEvent;
90
+ return /*#__PURE__*/_jsx(View, {
91
+ style: styles.fullPageSection,
92
+ children: /*#__PURE__*/_jsx(View, {
93
+ style: styles.contentCard,
94
+ children: renderRouteContent(eventToShow, "CURRENT ROUTE STATE")
95
+ })
96
+ });
97
+ };
98
+
99
+ // Render diff tab
100
+ const renderDiff = () => {
101
+ if (navigationItems.length === 0) {
102
+ return /*#__PURE__*/_jsxs(View, {
103
+ style: styles.emptyState,
104
+ children: [/*#__PURE__*/_jsx(AlertCircle, {
105
+ size: 32,
106
+ color: buoyColors.textMuted
107
+ }), /*#__PURE__*/_jsx(Text, {
108
+ style: styles.emptyText,
109
+ children: "No changes to display"
110
+ })]
111
+ });
112
+ }
113
+ const prevIndex = Math.max(0, selectedEventIndex - 1);
114
+ const currentIndex = selectedEventIndex;
115
+ const previousEvent = navigationItems[prevIndex];
116
+ const currentEvent = navigationItems[currentIndex];
117
+ return /*#__PURE__*/_jsx(ScrollView, {
118
+ style: styles.fullPageSection,
119
+ children: /*#__PURE__*/_jsxs(View, {
120
+ style: styles.diffContainer,
121
+ children: [/*#__PURE__*/_jsxs(View, {
122
+ style: styles.diffSection,
123
+ children: [/*#__PURE__*/_jsx(Text, {
124
+ style: styles.diffSectionTitle,
125
+ children: "PREVIOUS"
126
+ }), /*#__PURE__*/_jsx(View, {
127
+ style: styles.contentCard,
128
+ children: renderRouteContent(previousEvent, `Event #${prevIndex + 1}`)
129
+ })]
130
+ }), /*#__PURE__*/_jsx(View, {
131
+ style: styles.diffDivider
132
+ }), /*#__PURE__*/_jsxs(View, {
133
+ style: styles.diffSection,
134
+ children: [/*#__PURE__*/_jsx(Text, {
135
+ style: styles.diffSectionTitle,
136
+ children: "CURRENT"
137
+ }), /*#__PURE__*/_jsx(View, {
138
+ style: styles.contentCard,
139
+ children: renderRouteContent(currentEvent, `Event #${currentIndex + 1}`)
140
+ })]
141
+ })]
142
+ })
143
+ });
144
+ };
145
+ return /*#__PURE__*/_jsxs(_Fragment, {
146
+ children: [/*#__PURE__*/_jsxs(View, {
147
+ style: [styles.contentOnly, {
148
+ flex: 1,
149
+ paddingBottom: !disableInternalFooter && totalEvents > 1 ? 80 : 0
150
+ }],
151
+ children: [/*#__PURE__*/_jsxs(View, {
152
+ style: styles.viewToggleContainer,
153
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
154
+ style: [styles.viewToggleCard, internalActiveView === "current" && styles.viewToggleCardActive],
155
+ onPress: () => handleViewChange("current"),
156
+ activeOpacity: 0.8,
157
+ children: [/*#__PURE__*/_jsxs(View, {
158
+ style: styles.viewToggleContent,
159
+ children: [/*#__PURE__*/_jsx(NavigationIcon, {
160
+ size: 16,
161
+ color: internalActiveView === "current" ? buoyColors.primary : buoyColors.textSecondary
162
+ }), /*#__PURE__*/_jsx(Text, {
163
+ style: [styles.viewToggleLabel, internalActiveView === "current" && styles.viewToggleLabelActive],
164
+ children: "CURRENT STATE"
165
+ })]
166
+ }), /*#__PURE__*/_jsx(Text, {
167
+ style: [styles.viewToggleDescription, internalActiveView === "current" && {
168
+ color: buoyColors.text
169
+ }],
170
+ children: "View the current route state"
171
+ })]
172
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
173
+ style: [styles.viewToggleCard, internalActiveView === "diff" && styles.viewToggleCardActive],
174
+ onPress: () => handleViewChange("diff"),
175
+ activeOpacity: 0.8,
176
+ children: [/*#__PURE__*/_jsxs(View, {
177
+ style: styles.viewToggleContent,
178
+ children: [/*#__PURE__*/_jsx(GitBranch, {
179
+ size: 16,
180
+ color: internalActiveView === "diff" ? buoyColors.success : buoyColors.textSecondary
181
+ }), /*#__PURE__*/_jsx(Text, {
182
+ style: [styles.viewToggleLabel, internalActiveView === "diff" && styles.viewToggleLabelActive],
183
+ children: "DIFF VIEW"
184
+ })]
185
+ }), /*#__PURE__*/_jsx(Text, {
186
+ style: [styles.viewToggleDescription, internalActiveView === "diff" && {
187
+ color: buoyColors.text
188
+ }],
189
+ children: "Compare changes between events"
190
+ })]
191
+ })]
192
+ }), internalActiveView === "current" && renderCurrentValue(), internalActiveView === "diff" && renderDiff()]
193
+ }), totalEvents > 1 && !disableInternalFooter && /*#__PURE__*/_jsxs(View, {
194
+ style: styles.stickyFooter,
195
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
196
+ onPress: () => onEventIndexChange(Math.max(0, selectedEventIndex - 1)),
197
+ disabled: selectedEventIndex === 0,
198
+ style: [styles.navButton, selectedEventIndex === 0 && styles.navButtonDisabled],
199
+ children: [/*#__PURE__*/_jsx(ChevronLeft, {
200
+ size: 20,
201
+ color: selectedEventIndex === 0 ? buoyColors.textMuted : buoyColors.text
202
+ }), /*#__PURE__*/_jsx(Text, {
203
+ style: [styles.navButtonText, selectedEventIndex === 0 && styles.navButtonTextDisabled],
204
+ children: "Previous"
205
+ })]
206
+ }), /*#__PURE__*/_jsxs(View, {
207
+ style: styles.eventCounterContainer,
208
+ children: [/*#__PURE__*/_jsxs(Text, {
209
+ style: styles.eventCounter,
210
+ children: ["Event ", selectedEventIndex + 1, " of ", totalEvents]
211
+ }), /*#__PURE__*/_jsx(Text, {
212
+ style: styles.eventTimestamp,
213
+ children: formatRelativeTime(new Date(navigationItems[selectedEventIndex]?.timestamp))
214
+ })]
215
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
216
+ onPress: () => onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1)),
217
+ disabled: selectedEventIndex === totalEvents - 1,
218
+ style: [styles.navButton, selectedEventIndex === totalEvents - 1 && styles.navButtonDisabled],
219
+ children: [/*#__PURE__*/_jsx(Text, {
220
+ style: [styles.navButtonText, selectedEventIndex === totalEvents - 1 && styles.navButtonTextDisabled],
221
+ children: "Next"
222
+ }), /*#__PURE__*/_jsx(ChevronRight, {
223
+ size: 20,
224
+ color: selectedEventIndex === totalEvents - 1 ? buoyColors.textMuted : buoyColors.text
225
+ })]
226
+ })]
227
+ })]
228
+ });
229
+ }
230
+
231
+ // External footer component to be rendered by the modal outside the ScrollView
232
+ export function RouteEventDetailFooter({
233
+ conversation,
234
+ selectedEventIndex = 0,
235
+ onEventIndexChange = () => {}
236
+ }) {
237
+ const navigationItems = conversation.events.sort((a, b) => a.timestamp - b.timestamp);
238
+ const totalEvents = navigationItems.length;
239
+ if (totalEvents <= 1) return null;
240
+ return /*#__PURE__*/_jsxs(View, {
241
+ style: styles.externalFooterBar,
242
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
243
+ onPress: () => onEventIndexChange(Math.max(0, selectedEventIndex - 1)),
244
+ disabled: selectedEventIndex === 0,
245
+ style: [styles.navButton, selectedEventIndex === 0 && styles.navButtonDisabled],
246
+ children: [/*#__PURE__*/_jsx(ChevronLeft, {
247
+ size: 20,
248
+ color: selectedEventIndex === 0 ? buoyColors.textMuted : buoyColors.text
249
+ }), /*#__PURE__*/_jsx(Text, {
250
+ style: [styles.navButtonText, selectedEventIndex === 0 && styles.navButtonTextDisabled],
251
+ children: "Previous"
252
+ })]
253
+ }), /*#__PURE__*/_jsxs(View, {
254
+ style: styles.eventCounterContainer,
255
+ children: [/*#__PURE__*/_jsxs(Text, {
256
+ style: styles.eventCounter,
257
+ children: ["Event ", selectedEventIndex + 1, " of ", totalEvents]
258
+ }), /*#__PURE__*/_jsx(Text, {
259
+ style: styles.eventTimestamp,
260
+ children: formatRelativeTime(new Date(navigationItems[selectedEventIndex]?.timestamp))
261
+ })]
262
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
263
+ onPress: () => onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1)),
264
+ disabled: selectedEventIndex === totalEvents - 1,
265
+ style: [styles.navButton, selectedEventIndex === totalEvents - 1 && styles.navButtonDisabled],
266
+ children: [/*#__PURE__*/_jsx(Text, {
267
+ style: [styles.navButtonText, selectedEventIndex === totalEvents - 1 && styles.navButtonTextDisabled],
268
+ children: "Next"
269
+ }), /*#__PURE__*/_jsx(ChevronRight, {
270
+ size: 20,
271
+ color: selectedEventIndex === totalEvents - 1 ? buoyColors.textMuted : buoyColors.text
272
+ })]
273
+ })]
274
+ });
275
+ }
276
+ const styles = StyleSheet.create({
277
+ contentOnly: {
278
+ flex: 1,
279
+ backgroundColor: buoyColors.base
280
+ },
281
+ stickyFooter: {
282
+ position: "absolute",
283
+ bottom: 0,
284
+ left: 0,
285
+ right: 0,
286
+ flexDirection: "row",
287
+ alignItems: "center",
288
+ justifyContent: "space-between",
289
+ paddingHorizontal: 16,
290
+ paddingVertical: 12,
291
+ backgroundColor: buoyColors.base,
292
+ borderTopWidth: 1,
293
+ borderTopColor: buoyColors.border,
294
+ shadowColor: "#000",
295
+ shadowOffset: {
296
+ width: 0,
297
+ height: -2
298
+ },
299
+ shadowOpacity: 0.1,
300
+ shadowRadius: 4,
301
+ elevation: 5
302
+ },
303
+ externalFooterBar: {
304
+ flexDirection: "row",
305
+ alignItems: "center",
306
+ justifyContent: "space-between",
307
+ paddingHorizontal: 16,
308
+ paddingVertical: 12,
309
+ backgroundColor: buoyColors.base,
310
+ borderTopWidth: 1,
311
+ borderTopColor: buoyColors.border,
312
+ shadowColor: "#000",
313
+ shadowOffset: {
314
+ width: 0,
315
+ height: -2
316
+ },
317
+ shadowOpacity: 0.1,
318
+ shadowRadius: 4,
319
+ elevation: 5
320
+ },
321
+ fullPageSection: {
322
+ flex: 1,
323
+ paddingHorizontal: 14,
324
+ paddingVertical: 10
325
+ },
326
+ emptyState: {
327
+ flex: 1,
328
+ alignItems: "center",
329
+ justifyContent: "center",
330
+ paddingVertical: 48
331
+ },
332
+ emptyText: {
333
+ marginTop: 12,
334
+ fontSize: 14,
335
+ color: buoyColors.textSecondary,
336
+ fontFamily: "monospace"
337
+ },
338
+ valueContent: {
339
+ marginTop: 4
340
+ },
341
+ valueHeader: {
342
+ flexDirection: "row",
343
+ alignItems: "center",
344
+ justifyContent: "space-between",
345
+ marginBottom: 4
346
+ },
347
+ valueLabel: {
348
+ fontSize: 10,
349
+ color: buoyColors.textSecondary,
350
+ fontFamily: "monospace",
351
+ letterSpacing: 0.5,
352
+ fontWeight: "600",
353
+ textTransform: "uppercase"
354
+ },
355
+ valueBox: {
356
+ backgroundColor: buoyColors.card,
357
+ borderRadius: 6,
358
+ borderWidth: 1,
359
+ borderColor: buoyColors.border,
360
+ padding: 8
361
+ },
362
+ navButton: {
363
+ flexDirection: "row",
364
+ alignItems: "center",
365
+ gap: 6,
366
+ paddingHorizontal: 12,
367
+ paddingVertical: 8,
368
+ borderRadius: 6,
369
+ backgroundColor: buoyColors.card,
370
+ minWidth: 100,
371
+ justifyContent: "center"
372
+ },
373
+ navButtonDisabled: {
374
+ opacity: 0.3
375
+ },
376
+ navButtonText: {
377
+ fontSize: 12,
378
+ fontWeight: "600",
379
+ color: buoyColors.text,
380
+ fontFamily: "monospace",
381
+ textTransform: "uppercase"
382
+ },
383
+ navButtonTextDisabled: {
384
+ color: buoyColors.textMuted
385
+ },
386
+ eventCounterContainer: {
387
+ alignItems: "center"
388
+ },
389
+ eventCounter: {
390
+ fontSize: 14,
391
+ fontWeight: "700",
392
+ color: buoyColors.text,
393
+ fontFamily: "monospace",
394
+ textTransform: "uppercase"
395
+ },
396
+ eventTimestamp: {
397
+ fontSize: 11,
398
+ color: buoyColors.textSecondary,
399
+ fontFamily: "monospace",
400
+ marginTop: 2
401
+ },
402
+ contentCard: {
403
+ backgroundColor: buoyColors.card,
404
+ borderRadius: 14,
405
+ padding: 14,
406
+ borderWidth: 1,
407
+ borderColor: buoyColors.border,
408
+ shadowColor: "#000",
409
+ shadowOffset: {
410
+ width: 0,
411
+ height: 2
412
+ },
413
+ shadowOpacity: 0.04,
414
+ shadowRadius: 16,
415
+ elevation: 2
416
+ },
417
+ // View Toggle Cards
418
+ viewToggleContainer: {
419
+ flexDirection: "row",
420
+ gap: 12,
421
+ padding: 14,
422
+ backgroundColor: buoyColors.base
423
+ },
424
+ viewToggleCard: {
425
+ flex: 1,
426
+ backgroundColor: buoyColors.card,
427
+ borderRadius: 14,
428
+ borderWidth: 1,
429
+ borderColor: buoyColors.border,
430
+ padding: 14,
431
+ gap: 8
432
+ },
433
+ viewToggleCardActive: {
434
+ borderWidth: 1.5,
435
+ borderColor: buoyColors.primary,
436
+ backgroundColor: buoyColors.primary + "15",
437
+ shadowColor: buoyColors.primary,
438
+ shadowOffset: {
439
+ width: 0,
440
+ height: 2
441
+ },
442
+ shadowOpacity: 0.1,
443
+ shadowRadius: 8,
444
+ elevation: 3
445
+ },
446
+ viewToggleContent: {
447
+ flexDirection: "row",
448
+ alignItems: "center",
449
+ gap: 8
450
+ },
451
+ viewToggleLabel: {
452
+ fontSize: 12,
453
+ fontWeight: "700",
454
+ letterSpacing: 0.5,
455
+ color: buoyColors.textSecondary,
456
+ textTransform: "uppercase"
457
+ },
458
+ viewToggleLabelActive: {
459
+ color: buoyColors.text
460
+ },
461
+ viewToggleDescription: {
462
+ fontSize: 11,
463
+ color: buoyColors.textMuted,
464
+ lineHeight: 16
465
+ },
466
+ // Diff view styles
467
+ diffContainer: {
468
+ gap: 16
469
+ },
470
+ diffSection: {
471
+ gap: 8
472
+ },
473
+ diffSectionTitle: {
474
+ fontSize: 10,
475
+ fontWeight: "700",
476
+ color: buoyColors.textSecondary,
477
+ fontFamily: "monospace",
478
+ letterSpacing: 0.5,
479
+ textTransform: "uppercase",
480
+ paddingHorizontal: 4
481
+ },
482
+ diffDivider: {
483
+ height: 1,
484
+ backgroundColor: buoyColors.border,
485
+ marginVertical: 8
486
+ }
487
+ });
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * RouteEventExpandedContent - Expanded view for route event details
5
+ *
6
+ * Shows 3 organized sections:
7
+ * - Section A: Route Information (Template, From, To)
8
+ * - Section B: Timing Information (Duration, Time)
9
+ * - Section C: Parameters & Metadata (Segments, Parameters, Visit count)
10
+ */
11
+
12
+ import { View, Text, StyleSheet } from "react-native";
13
+ import { InlineCopyButton, buoyColors } from "@buoy-gg/shared-ui";
14
+ import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ // Format duration in milliseconds to human-readable string
17
+ function formatDuration(ms) {
18
+ if (ms < 1000) {
19
+ return `${ms}ms`;
20
+ }
21
+ if (ms < 60000) {
22
+ return `${(ms / 1000).toFixed(1)}s`;
23
+ }
24
+ const minutes = Math.floor(ms / 60000);
25
+ const seconds = (ms % 60000 / 1000).toFixed(0);
26
+ return `${minutes}m ${seconds}s`;
27
+ }
28
+ export function RouteEventExpandedContent({
29
+ event,
30
+ visitNumber,
31
+ routeTemplate
32
+ }) {
33
+ const hasParams = event.params && Object.keys(event.params).length > 0;
34
+ return /*#__PURE__*/_jsxs(View, {
35
+ style: styles.container,
36
+ children: [/*#__PURE__*/_jsxs(View, {
37
+ style: styles.section,
38
+ children: [routeTemplate && /*#__PURE__*/_jsxs(View, {
39
+ style: styles.detailRow,
40
+ children: [/*#__PURE__*/_jsx(Text, {
41
+ style: styles.detailLabel,
42
+ children: "Template:"
43
+ }), /*#__PURE__*/_jsx(Text, {
44
+ style: styles.detailValue,
45
+ children: routeTemplate
46
+ }), /*#__PURE__*/_jsx(InlineCopyButton, {
47
+ value: routeTemplate,
48
+ buttonStyle: styles.copyButton
49
+ })]
50
+ }), event.previousPathname && /*#__PURE__*/_jsxs(View, {
51
+ style: styles.detailRow,
52
+ children: [/*#__PURE__*/_jsx(Text, {
53
+ style: styles.detailLabel,
54
+ children: "From:"
55
+ }), /*#__PURE__*/_jsx(Text, {
56
+ style: styles.detailValue,
57
+ children: event.previousPathname
58
+ }), /*#__PURE__*/_jsx(InlineCopyButton, {
59
+ value: event.previousPathname,
60
+ buttonStyle: styles.copyButton
61
+ })]
62
+ }), /*#__PURE__*/_jsxs(View, {
63
+ style: styles.detailRow,
64
+ children: [/*#__PURE__*/_jsx(Text, {
65
+ style: styles.detailLabel,
66
+ children: "To:"
67
+ }), /*#__PURE__*/_jsx(Text, {
68
+ style: styles.detailValue,
69
+ children: event.pathname
70
+ }), /*#__PURE__*/_jsx(InlineCopyButton, {
71
+ value: event.pathname,
72
+ buttonStyle: styles.copyButton
73
+ })]
74
+ })]
75
+ }), /*#__PURE__*/_jsxs(View, {
76
+ style: styles.section,
77
+ children: [event.timeSincePrevious !== undefined && /*#__PURE__*/_jsxs(View, {
78
+ style: styles.detailRow,
79
+ children: [/*#__PURE__*/_jsx(Text, {
80
+ style: styles.detailLabel,
81
+ children: "Duration:"
82
+ }), /*#__PURE__*/_jsx(Text, {
83
+ style: styles.detailValue,
84
+ children: formatDuration(event.timeSincePrevious)
85
+ })]
86
+ }), /*#__PURE__*/_jsxs(View, {
87
+ style: styles.detailRow,
88
+ children: [/*#__PURE__*/_jsx(Text, {
89
+ style: styles.detailLabel,
90
+ children: "Time:"
91
+ }), /*#__PURE__*/_jsx(Text, {
92
+ style: styles.detailValue,
93
+ children: new Date(event.timestamp).toLocaleString()
94
+ })]
95
+ })]
96
+ }), /*#__PURE__*/_jsxs(View, {
97
+ style: styles.section,
98
+ children: [event.segments && event.segments.length > 0 && /*#__PURE__*/_jsxs(View, {
99
+ style: styles.detailRow,
100
+ children: [/*#__PURE__*/_jsx(Text, {
101
+ style: styles.detailLabel,
102
+ children: "Segments:"
103
+ }), /*#__PURE__*/_jsx(Text, {
104
+ style: styles.detailValue,
105
+ children: event.segments.join(' → ')
106
+ }), /*#__PURE__*/_jsx(InlineCopyButton, {
107
+ value: JSON.stringify(event.segments),
108
+ buttonStyle: styles.copyButton
109
+ })]
110
+ }), hasParams && /*#__PURE__*/_jsxs(View, {
111
+ style: styles.dataViewerContainer,
112
+ children: [/*#__PURE__*/_jsxs(View, {
113
+ style: styles.dataViewerHeader,
114
+ children: [/*#__PURE__*/_jsxs(Text, {
115
+ style: styles.dataViewerTitle,
116
+ children: ["Parameters (", Object.keys(event.params).length, ")"]
117
+ }), /*#__PURE__*/_jsx(InlineCopyButton, {
118
+ value: JSON.stringify(event.params, null, 2),
119
+ buttonStyle: styles.copyButton
120
+ })]
121
+ }), /*#__PURE__*/_jsx(DataViewer, {
122
+ data: event.params,
123
+ title: "",
124
+ showTypeFilter: false
125
+ })]
126
+ }), visitNumber > 1 && /*#__PURE__*/_jsxs(View, {
127
+ style: styles.detailRow,
128
+ children: [/*#__PURE__*/_jsx(Text, {
129
+ style: styles.detailLabel,
130
+ children: "Visited:"
131
+ }), /*#__PURE__*/_jsxs(Text, {
132
+ style: styles.detailValue,
133
+ children: [visitNumber, " time", visitNumber !== 1 ? 's' : '']
134
+ })]
135
+ })]
136
+ })]
137
+ });
138
+ }
139
+ const styles = StyleSheet.create({
140
+ container: {
141
+ gap: 12
142
+ },
143
+ section: {
144
+ gap: 8
145
+ },
146
+ detailRow: {
147
+ flexDirection: "row",
148
+ alignItems: "center",
149
+ gap: 8
150
+ },
151
+ detailLabel: {
152
+ fontSize: 10,
153
+ color: buoyColors.textSecondary,
154
+ fontFamily: "monospace",
155
+ fontWeight: "600",
156
+ minWidth: 70
157
+ },
158
+ detailValue: {
159
+ fontSize: 11,
160
+ color: buoyColors.text,
161
+ fontFamily: "monospace",
162
+ flex: 1
163
+ },
164
+ copyButton: {
165
+ padding: 4,
166
+ borderRadius: 4
167
+ },
168
+ dataViewerContainer: {
169
+ marginTop: 4
170
+ },
171
+ dataViewerHeader: {
172
+ flexDirection: "row",
173
+ alignItems: "center",
174
+ justifyContent: "space-between",
175
+ marginBottom: 8
176
+ },
177
+ dataViewerTitle: {
178
+ fontSize: 11,
179
+ color: buoyColors.textSecondary,
180
+ fontFamily: "monospace",
181
+ fontWeight: "600"
182
+ }
183
+ });