@buoy-gg/highlight-updates 3.0.1 → 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.
Files changed (83) hide show
  1. package/lib/commonjs/highlight-updates/HighlightUpdatesOverlay.js +285 -1
  2. package/lib/commonjs/highlight-updates/components/HighlightFilterView.js +1371 -1
  3. package/lib/commonjs/highlight-updates/components/HighlightUpdatesModal.js +564 -1
  4. package/lib/commonjs/highlight-updates/components/IdentifierBadge.js +267 -1
  5. package/lib/commonjs/highlight-updates/components/IsolatedRenderList.js +178 -1
  6. package/lib/commonjs/highlight-updates/components/ModalHeaderContent.js +309 -1
  7. package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +500 -1
  8. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +803 -1
  9. package/lib/commonjs/highlight-updates/components/RenderHistoryViewer.js +894 -1
  10. package/lib/commonjs/highlight-updates/components/RenderListItem.js +220 -1
  11. package/lib/commonjs/highlight-updates/components/RendersCopySettingsView.js +562 -1
  12. package/lib/commonjs/highlight-updates/components/StatsDisplay.js +70 -1
  13. package/lib/commonjs/highlight-updates/components/index.js +97 -1
  14. package/lib/commonjs/highlight-updates/types/copySettings.js +107 -1
  15. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1819 -1
  16. package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +359 -1
  17. package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +371 -1
  18. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1828 -1
  19. package/lib/commonjs/highlight-updates/utils/RenderTracker.js +919 -1
  20. package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +264 -1
  21. package/lib/commonjs/highlight-updates/utils/copySettingsStorage.js +49 -1
  22. package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +58 -1
  23. package/lib/commonjs/highlight-updates/utils/rendersExportFormatter.js +485 -1
  24. package/lib/commonjs/index.js +320 -1
  25. package/lib/commonjs/preset.js +278 -1
  26. package/lib/commonjs/sync/highlightUpdatesSyncAdapter.js +83 -0
  27. package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +278 -1
  28. package/lib/module/highlight-updates/components/HighlightFilterView.js +1365 -1
  29. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +558 -1
  30. package/lib/module/highlight-updates/components/IdentifierBadge.js +259 -1
  31. package/lib/module/highlight-updates/components/IsolatedRenderList.js +174 -1
  32. package/lib/module/highlight-updates/components/ModalHeaderContent.js +304 -1
  33. package/lib/module/highlight-updates/components/RenderCauseBadge.js +491 -1
  34. package/lib/module/highlight-updates/components/RenderDetailView.js +797 -1
  35. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +888 -1
  36. package/lib/module/highlight-updates/components/RenderListItem.js +215 -1
  37. package/lib/module/highlight-updates/components/RendersCopySettingsView.js +558 -1
  38. package/lib/module/highlight-updates/components/StatsDisplay.js +67 -1
  39. package/lib/module/highlight-updates/components/index.js +16 -1
  40. package/lib/module/highlight-updates/types/copySettings.js +102 -1
  41. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1815 -1
  42. package/lib/module/highlight-updates/utils/PerformanceLogger.js +353 -1
  43. package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +358 -1
  44. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1818 -1
  45. package/lib/module/highlight-updates/utils/RenderTracker.js +916 -1
  46. package/lib/module/highlight-updates/utils/ViewTypeMapper.js +255 -1
  47. package/lib/module/highlight-updates/utils/copySettingsStorage.js +43 -1
  48. package/lib/module/highlight-updates/utils/renderExportFormatter.js +54 -1
  49. package/lib/module/highlight-updates/utils/rendersExportFormatter.js +478 -1
  50. package/lib/module/index.js +74 -1
  51. package/lib/module/preset.js +272 -1
  52. package/lib/module/sync/highlightUpdatesSyncAdapter.js +78 -0
  53. package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +1 -0
  54. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +1 -0
  55. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +1 -0
  56. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +1 -0
  57. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +1 -0
  58. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +1 -0
  59. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +1 -0
  60. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -0
  61. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -0
  62. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +1 -0
  63. package/lib/typescript/highlight-updates/components/RendersCopySettingsView.d.ts.map +1 -0
  64. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +1 -0
  65. package/lib/typescript/highlight-updates/components/index.d.ts.map +1 -0
  66. package/lib/typescript/highlight-updates/types/copySettings.d.ts.map +1 -0
  67. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts +90 -0
  68. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -0
  69. package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +1 -0
  70. package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +1 -0
  71. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +1 -0
  72. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts +10 -0
  73. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +1 -0
  74. package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +1 -0
  75. package/lib/typescript/highlight-updates/utils/copySettingsStorage.d.ts.map +1 -0
  76. package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +1 -0
  77. package/lib/typescript/highlight-updates/utils/rendersExportFormatter.d.ts.map +1 -0
  78. package/lib/typescript/index.d.ts +1 -0
  79. package/lib/typescript/index.d.ts.map +1 -0
  80. package/lib/typescript/preset.d.ts.map +1 -0
  81. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts +36 -0
  82. package/lib/typescript/sync/highlightUpdatesSyncAdapter.d.ts.map +1 -0
  83. package/package.json +7 -7
@@ -1 +1,797 @@
1
- "use strict";import React,{useMemo,useState,useCallback}from"react";import{View,Text,TouchableOpacity,ScrollView,StyleSheet}from"react-native";import{CopyButton,formatRelativeTime,PlusIcon,MinusIcon,EventStepperFooter,Lock,buoyColors}from"@buoy-gg/shared-ui";import{TreeDiffViewer}from"@buoy-gg/shared-ui/dataViewer";import{CAUSE_CONFIG,COMPONENT_CAUSE_CONFIG}from"./RenderCauseBadge";import{generateSingleComponentExport}from"../utils/rendersExportFormatter";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";const FREE_TIER_EVENT_LIMIT=5;export function RenderDetailView({render:e,disableInternalFooter:t=!1,selectedEventIndex:o,onEventIndexChange:s,onAddFilter:r,isPro:l=!1}){const[n,i]=useState(0),a=o??n,d=s??i,c=useMemo(()=>e.renderHistory&&0!==e.renderHistory.length?[...e.renderHistory].sort((e,t)=>e.timestamp-t.timestamp):[],[e.renderHistory]),u=c.length,y=c[a],b=useMemo(()=>l?0:Math.max(0,u-5),[l,u]),x=y?.cause||e.lastRenderCause,p=useCallback(()=>{a>0&&d(a-1)},[a,d]),m=useCallback(()=>{const e=l?u-1:Math.min(4,u-1);a<e&&d(a+1)},[a,u,d,l]),C=useMemo(()=>generateSingleComponentExport(e),[e]),h=e.componentName||e.displayName,g=e.viewType,f=e.lastRenderTime-e.firstRenderTime,j=f>0?(e.renderCount/(f/1e3)).toFixed(1):e.renderCount.toString(),_=!t&&u>1;return _jsxs(View,{style:styles.container,children:[_jsxs(View,{style:styles.header,children:[_jsxs(View,{style:styles.headerLeft,children:[_jsx(Text,{style:styles.componentName,numberOfLines:1,children:h}),_jsx(View,{style:styles.nativeTypeBadge,children:_jsx(Text,{style:styles.nativeTypeText,children:g})})]}),_jsx(CopyButton,{value:C,size:14})]}),_jsxs(ScrollView,{style:styles.scrollView,contentContainerStyle:[styles.scrollContent,_&&{paddingBottom:100}],showsVerticalScrollIndicator:!1,children:[x&&_jsx(AnswerCard,{cause:x,renderNumber:y?.renderNumber}),_jsx(DetailsSection,{render:e,rendersPerSec:j}),b>0&&_jsxs(View,{style:styles.lockedBanner,children:[_jsx(Lock,{size:14,color:buoyColors.warning}),_jsxs(Text,{style:styles.lockedBannerText,children:[b," ",1===b?"event":"events"," hidden"]}),_jsx(Text,{style:styles.lockedBannerSubtext,children:"Upgrade to Pro"})]}),r&&_jsx(QuickActionsSection,{render:e,onAddFilter:r})]}),_&&_jsx(EventStepperFooter,{currentIndex:a,totalItems:u,onPrevious:p,onNext:m,itemLabel:"Render",subtitle:y?.timestamp?formatRelativeTime(new Date(y.timestamp)):void 0,applySafeAreaInset:!1,absolute:!0})]})}function AnswerCard({cause:e,renderNumber:t}){const o=e.componentCause||e.type,s=e.componentCause?COMPONENT_CAUSE_CONFIG[e.componentCause]:CAUSE_CONFIG[e.type],r=e.hookChanges&&e.hookChanges.length>0,l=e.changedKeys&&e.changedKeys.length>0;return _jsxs(View,{style:styles.answerCard,children:[_jsxs(View,{style:styles.causeBadgeRow,children:[_jsx(Text,{style:styles.causeLabel,children:"mount"===e.type?"First render":"parent"===o?"Triggered by":"Changed:"}),_jsx(View,{style:[styles.causeBadgeLarge,{backgroundColor:s.color+"20"}],children:_jsx(Text,{style:[styles.causeBadgeLargeText,{color:s.color}],children:s.label.toUpperCase()})})]}),r&&_jsx(View,{style:styles.hookChangesContainer,children:e.hookChanges.map((e,t)=>{const o=`${e.type}[${e.index}]`,s={[o]:e.previousValue},r={[o]:e.currentValue};return _jsx(View,{style:styles.hookDiffContainer,children:_jsx(TreeDiffViewer,{oldValue:s,newValue:r,theme:"dark",showUnchanged:!1})},t)})}),!r&&l&&_jsx(View,{style:styles.propsChangesContainer,children:e.changedKeys.filter(e=>!e.includes("(ref only)")).slice(0,5).map((e,t)=>_jsx(View,{style:styles.propChangeChip,children:_jsx(Text,{style:styles.propChangeText,children:e})},t))})]})}function DetailsSection({render:e,rendersPerSec:t}){const o=e.testID||e.nativeID||e.accessibilityLabel,s=e.measurements;return _jsxs(View,{style:styles.detailsSection,children:[o&&_jsxs(View,{style:styles.detailsCard,children:[_jsx(Text,{style:styles.detailsCardTitle,children:"Identifiers"}),e.testID&&_jsx(DetailRow,{label:"testID",value:e.testID}),e.nativeID&&_jsx(DetailRow,{label:"nativeID",value:e.nativeID}),e.accessibilityLabel&&_jsx(DetailRow,{label:"a11y",value:e.accessibilityLabel})]}),_jsxs(View,{style:styles.statsRow,children:[_jsxs(View,{style:styles.statItem,children:[_jsx(Text,{style:[styles.statValue,{color:e.color}],children:e.renderCount}),_jsx(Text,{style:styles.statLabel,children:"renders"})]}),_jsx(View,{style:styles.statDivider}),_jsxs(View,{style:styles.statItem,children:[_jsx(Text,{style:styles.statValue,children:t}),_jsx(Text,{style:styles.statLabel,children:"/sec"})]}),_jsx(View,{style:styles.statDivider}),_jsxs(View,{style:styles.statItem,children:[_jsx(Text,{style:styles.statValue,children:e.nativeTag}),_jsx(Text,{style:styles.statLabel,children:"tag"})]})]}),s&&_jsxs(View,{style:styles.measurementsRow,children:[_jsxs(View,{style:styles.measurementItem,children:[_jsx(Text,{style:styles.measurementLabel,children:"x"}),_jsx(Text,{style:styles.measurementValue,children:Math.round(e.measurements.x)})]}),_jsxs(View,{style:styles.measurementItem,children:[_jsx(Text,{style:styles.measurementLabel,children:"y"}),_jsx(Text,{style:styles.measurementValue,children:Math.round(e.measurements.y)})]}),_jsxs(View,{style:styles.measurementItem,children:[_jsx(Text,{style:styles.measurementLabel,children:"w"}),_jsx(Text,{style:styles.measurementValue,children:Math.round(e.measurements.width)})]}),_jsxs(View,{style:styles.measurementItem,children:[_jsx(Text,{style:styles.measurementLabel,children:"h"}),_jsx(Text,{style:styles.measurementValue,children:Math.round(e.measurements.height)})]})]})]})}function DetailRow({label:e,value:t}){return _jsxs(View,{style:styles.detailRow,children:[_jsx(Text,{style:styles.detailLabel,children:e}),_jsx(Text,{style:styles.detailValue,numberOfLines:1,children:t})]})}function QuickActionsSection({render:e,onAddFilter:t}){const[o,s]=useState(null),r=useMemo(()=>{const t=[];return e.nativeID&&t.push({type:"nativeID",value:e.nativeID,label:"nativeID"}),e.testID&&t.push({type:"testID",value:e.testID,label:"testID"}),e.accessibilityLabel&&t.push({type:"accessibilityLabel",value:e.accessibilityLabel,label:"a11y"}),e.componentName&&t.push({type:"component",value:e.componentName,label:"component"}),t.push({type:"viewType",value:e.viewType,label:"viewType"}),t},[e]),l=null!==o?r[o]:null,n=useCallback(e=>{s(t=>t===e?null:e)},[]),i=useCallback(()=>{l&&(t({type:l.type,value:l.value},"include"),s(null))},[l,t]),a=useCallback(()=>{l&&(t({type:l.type,value:l.value},"exclude"),s(null))},[l,t]);return _jsxs(View,{style:styles.quickActionsSection,children:[_jsx(Text,{style:styles.quickActionsTitle,children:"Quick Filters"}),_jsx(View,{style:styles.filterOptionsList,children:r.map((e,t)=>_jsx(FilterOptionCard,{label:e.label,value:e.value,isSelected:o===t,onSelect:()=>n(t)},e.type))}),_jsxs(View,{style:styles.filterActionButtons,children:[_jsxs(TouchableOpacity,{style:[styles.filterActionButton,styles.filterActionButtonInclude,!l&&styles.filterActionButtonDisabled],onPress:i,disabled:!l,activeOpacity:.7,children:[_jsx(PlusIcon,{size:14,color:l?buoyColors.success:buoyColors.textMuted}),_jsx(Text,{style:[styles.filterActionButtonText,{color:l?buoyColors.success:buoyColors.textMuted}],children:"Only Show This"})]}),_jsxs(TouchableOpacity,{style:[styles.filterActionButton,styles.filterActionButtonExclude,!l&&styles.filterActionButtonDisabled],onPress:a,disabled:!l,activeOpacity:.7,children:[_jsx(MinusIcon,{size:14,color:l?buoyColors.error:buoyColors.textMuted}),_jsx(Text,{style:[styles.filterActionButtonText,{color:l?buoyColors.error:buoyColors.textMuted}],children:"Hide This"})]})]})]})}function FilterOptionCard({label:e,value:t,isSelected:o,onSelect:s}){return _jsxs(TouchableOpacity,{style:[styles.filterOptionCard,o&&styles.filterOptionCardSelected],onPress:s,activeOpacity:.7,children:[_jsx(Text,{style:[styles.filterOptionLabel,o&&styles.filterOptionLabelSelected],children:e}),_jsx(Text,{style:[styles.filterOptionValue,o&&styles.filterOptionValueSelected],numberOfLines:1,children:t})]})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:buoyColors.base},header:{flexDirection:"row",alignItems:"center",justifyContent:"center",gap:12,padding:16,paddingBottom:0},scrollView:{flex:1},scrollContent:{padding:16,paddingTop:12,paddingBottom:80,gap:12},headerLeft:{flex:1,flexDirection:"row",alignItems:"center",justifyContent:"center",gap:10},componentName:{fontSize:18,fontWeight:"700",color:buoyColors.text,flexShrink:1},nativeTypeBadge:{backgroundColor:buoyColors.input,paddingVertical:3,paddingHorizontal:8,borderRadius:4},nativeTypeText:{fontSize:11,fontWeight:"600",color:buoyColors.textMuted,fontFamily:"monospace"},answerCard:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:12,gap:6},causeBadgeRow:{flexDirection:"row",alignItems:"center",gap:8},causeBadgeLarge:{paddingVertical:4,paddingHorizontal:10,borderRadius:4},causeBadgeLargeText:{fontSize:11,fontWeight:"700",letterSpacing:.5},causeLabel:{fontSize:12,color:buoyColors.textSecondary,fontWeight:"500"},hookChangesContainer:{gap:6},hookDiffContainer:{backgroundColor:buoyColors.input,borderRadius:6,overflow:"hidden"},propsChangesContainer:{flexDirection:"row",flexWrap:"wrap",gap:4},propChangeChip:{backgroundColor:buoyColors.input,paddingVertical:2,paddingHorizontal:6,borderRadius:4},propChangeText:{fontSize:11,color:buoyColors.text,fontFamily:"monospace"},detailsSection:{gap:10},detailsCard:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:12,gap:6},detailsCardTitle:{fontSize:10,fontWeight:"600",color:buoyColors.textMuted,letterSpacing:.5,textTransform:"uppercase",marginBottom:4},detailRow:{flexDirection:"row",alignItems:"center",gap:8},detailLabel:{fontSize:11,fontWeight:"600",color:buoyColors.textSecondary,minWidth:55},detailValue:{fontSize:12,color:buoyColors.text,fontFamily:"monospace",flex:1},statsRow:{flexDirection:"row",alignItems:"center",justifyContent:"center",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,paddingVertical:10,paddingHorizontal:16},statItem:{flex:1,alignItems:"center"},statValue:{fontSize:16,fontWeight:"700",color:buoyColors.text,fontFamily:"monospace"},statLabel:{fontSize:10,color:buoyColors.textMuted,marginTop:2},statDivider:{width:1,height:24,backgroundColor:buoyColors.border,marginHorizontal:12},measurementsRow:{flexDirection:"row",alignItems:"center",justifyContent:"space-around",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,paddingVertical:8,paddingHorizontal:12},measurementItem:{flexDirection:"row",alignItems:"baseline",gap:4},measurementLabel:{fontSize:10,fontWeight:"600",color:buoyColors.textMuted,textTransform:"uppercase"},measurementValue:{fontSize:12,fontWeight:"600",color:buoyColors.text,fontFamily:"monospace"},quickActionsSection:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:12,gap:10},quickActionsTitle:{fontSize:10,fontWeight:"600",color:buoyColors.textMuted,letterSpacing:.5,textTransform:"uppercase",marginBottom:2},filterOptionsList:{gap:6},filterOptionCard:{flexDirection:"row",alignItems:"center",backgroundColor:buoyColors.input,borderRadius:6,paddingVertical:10,paddingHorizontal:12,borderWidth:1,borderColor:"transparent",gap:10},filterOptionCardSelected:{borderColor:buoyColors.success,backgroundColor:buoyColors.success+"15"},filterOptionLabel:{fontSize:10,fontWeight:"600",color:buoyColors.textMuted,minWidth:70},filterOptionLabelSelected:{color:buoyColors.success},filterOptionValue:{fontSize:12,color:buoyColors.text,fontFamily:"monospace",flex:1},filterOptionValueSelected:{color:buoyColors.text},filterActionButtons:{flexDirection:"row",gap:8,marginTop:12},filterActionButton:{flexDirection:"row",alignItems:"center",justifyContent:"center",gap:4,paddingVertical:8,paddingHorizontal:10,borderRadius:6,borderWidth:1},filterActionButtonInclude:{backgroundColor:buoyColors.success+"15",borderColor:buoyColors.success+"40"},filterActionButtonExclude:{backgroundColor:buoyColors.error+"15",borderColor:buoyColors.error+"40"},filterActionButtonDisabled:{backgroundColor:buoyColors.input,borderColor:buoyColors.border,opacity:.5},filterActionButtonText:{fontSize:11,fontWeight:"600"},lockedBanner:{flexDirection:"row",alignItems:"center",justifyContent:"center",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.warning+"40",paddingVertical:12,paddingHorizontal:16,gap:8},lockedBannerText:{color:buoyColors.warning,fontSize:13,fontWeight:"600"},lockedBannerSubtext:{color:buoyColors.textMuted,fontSize:12}});export default RenderDetailView;
1
+ "use strict";
2
+
3
+ /**
4
+ * RenderDetailView
5
+ *
6
+ * Minimal, glanceable view for render details.
7
+ * Design principle: Dev should understand WHY in 3 seconds.
8
+ *
9
+ * Layout:
10
+ * - Header: Component name + native type
11
+ * - Answer Card: Cause badges + hook change (THE answer)
12
+ * - History Row: Compact inline navigation
13
+ */
14
+
15
+ import React, { useMemo, useState, useCallback } from "react";
16
+ import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from "react-native";
17
+ import { CopyButton, formatRelativeTime, PlusIcon, MinusIcon, EventStepperFooter, Lock, buoyColors } from "@buoy-gg/shared-ui";
18
+ import { TreeDiffViewer } from "@buoy-gg/shared-ui/dataViewer";
19
+ import { CAUSE_CONFIG, COMPONENT_CAUSE_CONFIG } from "./RenderCauseBadge";
20
+ import { generateSingleComponentExport } from "../utils/rendersExportFormatter";
21
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
+ // Free tier limit for render history events
23
+ const FREE_TIER_EVENT_LIMIT = 5;
24
+ export function RenderDetailView({
25
+ render,
26
+ disableInternalFooter = false,
27
+ selectedEventIndex: externalIndex,
28
+ onEventIndexChange: externalOnChange,
29
+ onAddFilter,
30
+ isPro = false
31
+ }) {
32
+ // Internal state for event index when not controlled externally
33
+ const [internalIndex, setInternalIndex] = useState(0);
34
+
35
+ // Use external or internal state
36
+ const selectedEventIndex = externalIndex ?? internalIndex;
37
+ const onEventIndexChange = externalOnChange ?? setInternalIndex;
38
+
39
+ // Get events sorted by timestamp (oldest first)
40
+ const events = useMemo(() => {
41
+ if (!render.renderHistory || render.renderHistory.length === 0) {
42
+ return [];
43
+ }
44
+ return [...render.renderHistory].sort((a, b) => a.timestamp - b.timestamp);
45
+ }, [render.renderHistory]);
46
+ const totalEvents = events.length;
47
+ const currentEvent = events[selectedEventIndex];
48
+
49
+ // Calculate hidden events for free tier
50
+ const hiddenEventCount = useMemo(() => {
51
+ if (isPro) return 0;
52
+ return Math.max(0, totalEvents - FREE_TIER_EVENT_LIMIT);
53
+ }, [isPro, totalEvents]);
54
+
55
+ // Use current event's cause if available, otherwise fall back to lastRenderCause
56
+ const displayCause = currentEvent?.cause || render.lastRenderCause;
57
+
58
+ // Navigation handlers
59
+ const goToPrevious = useCallback(() => {
60
+ if (selectedEventIndex > 0) {
61
+ onEventIndexChange(selectedEventIndex - 1);
62
+ }
63
+ }, [selectedEventIndex, onEventIndexChange]);
64
+ const goToNext = useCallback(() => {
65
+ // Limit navigation for free tier users
66
+ const maxIndex = isPro ? totalEvents - 1 : Math.min(FREE_TIER_EVENT_LIMIT - 1, totalEvents - 1);
67
+ if (selectedEventIndex < maxIndex) {
68
+ onEventIndexChange(selectedEventIndex + 1);
69
+ }
70
+ }, [selectedEventIndex, totalEvents, onEventIndexChange, isPro]);
71
+
72
+ // Memoize copy data
73
+ const copyData = useMemo(() => generateSingleComponentExport(render), [render]);
74
+
75
+ // Get component name (prefer componentName, fall back to displayName)
76
+ const componentName = render.componentName || render.displayName;
77
+ const nativeType = render.viewType;
78
+
79
+ // Calculate render stats
80
+ const renderDuration = render.lastRenderTime - render.firstRenderTime;
81
+ const rendersPerSec = renderDuration > 0 ? (render.renderCount / (renderDuration / 1000)).toFixed(1) : render.renderCount.toString();
82
+
83
+ // Determine if we should show footer (either history or filters)
84
+ const showHistoryFooter = !disableInternalFooter && totalEvents > 1;
85
+ return /*#__PURE__*/_jsxs(View, {
86
+ style: styles.container,
87
+ children: [/*#__PURE__*/_jsxs(View, {
88
+ style: styles.header,
89
+ children: [/*#__PURE__*/_jsxs(View, {
90
+ style: styles.headerLeft,
91
+ children: [/*#__PURE__*/_jsx(Text, {
92
+ style: styles.componentName,
93
+ numberOfLines: 1,
94
+ children: componentName
95
+ }), /*#__PURE__*/_jsx(View, {
96
+ style: styles.nativeTypeBadge,
97
+ children: /*#__PURE__*/_jsx(Text, {
98
+ style: styles.nativeTypeText,
99
+ children: nativeType
100
+ })
101
+ })]
102
+ }), /*#__PURE__*/_jsx(CopyButton, {
103
+ value: copyData,
104
+ size: 14
105
+ })]
106
+ }), /*#__PURE__*/_jsxs(ScrollView, {
107
+ style: styles.scrollView,
108
+ contentContainerStyle: [styles.scrollContent, showHistoryFooter && {
109
+ paddingBottom: 100
110
+ }],
111
+ showsVerticalScrollIndicator: false,
112
+ children: [displayCause && /*#__PURE__*/_jsx(AnswerCard, {
113
+ cause: displayCause,
114
+ renderNumber: currentEvent?.renderNumber
115
+ }), /*#__PURE__*/_jsx(DetailsSection, {
116
+ render: render,
117
+ rendersPerSec: rendersPerSec
118
+ }), hiddenEventCount > 0 && /*#__PURE__*/_jsxs(View, {
119
+ style: styles.lockedBanner,
120
+ children: [/*#__PURE__*/_jsx(Lock, {
121
+ size: 14,
122
+ color: buoyColors.warning
123
+ }), /*#__PURE__*/_jsxs(Text, {
124
+ style: styles.lockedBannerText,
125
+ children: [hiddenEventCount, " ", hiddenEventCount === 1 ? 'event' : 'events', " hidden"]
126
+ }), /*#__PURE__*/_jsx(Text, {
127
+ style: styles.lockedBannerSubtext,
128
+ children: "Upgrade to Pro"
129
+ })]
130
+ }), onAddFilter && /*#__PURE__*/_jsx(QuickActionsSection, {
131
+ render: render,
132
+ onAddFilter: onAddFilter
133
+ })]
134
+ }), showHistoryFooter && /*#__PURE__*/_jsx(EventStepperFooter, {
135
+ currentIndex: selectedEventIndex,
136
+ totalItems: totalEvents,
137
+ onPrevious: goToPrevious,
138
+ onNext: goToNext,
139
+ itemLabel: "Render",
140
+ subtitle: currentEvent?.timestamp ? formatRelativeTime(new Date(currentEvent.timestamp)) : undefined,
141
+ applySafeAreaInset: false,
142
+ absolute: true
143
+ })]
144
+ });
145
+ }
146
+
147
+ /**
148
+ * AnswerCard - The hero section showing WHY the component rendered
149
+ * Design: Single badge + what changed below it
150
+ */
151
+ function AnswerCard({
152
+ cause,
153
+ renderNumber
154
+ }) {
155
+ // Use component cause if available, otherwise native cause
156
+ const displayCauseType = cause.componentCause || cause.type;
157
+ const config = cause.componentCause ? COMPONENT_CAUSE_CONFIG[cause.componentCause] : CAUSE_CONFIG[cause.type];
158
+ const hasHookChanges = cause.hookChanges && cause.hookChanges.length > 0;
159
+ const hasChangedKeys = cause.changedKeys && cause.changedKeys.length > 0;
160
+
161
+ // Get contextual label based on cause type
162
+ const getLabel = () => {
163
+ if (cause.type === "mount") return "First render";
164
+ if (displayCauseType === "parent") return "Triggered by";
165
+ return "Changed:";
166
+ };
167
+ return /*#__PURE__*/_jsxs(View, {
168
+ style: styles.answerCard,
169
+ children: [/*#__PURE__*/_jsxs(View, {
170
+ style: styles.causeBadgeRow,
171
+ children: [/*#__PURE__*/_jsx(Text, {
172
+ style: styles.causeLabel,
173
+ children: getLabel()
174
+ }), /*#__PURE__*/_jsx(View, {
175
+ style: [styles.causeBadgeLarge, {
176
+ backgroundColor: config.color + "20"
177
+ }],
178
+ children: /*#__PURE__*/_jsx(Text, {
179
+ style: [styles.causeBadgeLargeText, {
180
+ color: config.color
181
+ }],
182
+ children: config.label.toUpperCase()
183
+ })
184
+ })]
185
+ }), hasHookChanges && /*#__PURE__*/_jsx(View, {
186
+ style: styles.hookChangesContainer,
187
+ children: cause.hookChanges.map((hook, index) => {
188
+ const hookKey = `${hook.type}[${hook.index}]`;
189
+ const oldValue = {
190
+ [hookKey]: hook.previousValue
191
+ };
192
+ const newValue = {
193
+ [hookKey]: hook.currentValue
194
+ };
195
+ return /*#__PURE__*/_jsx(View, {
196
+ style: styles.hookDiffContainer,
197
+ children: /*#__PURE__*/_jsx(TreeDiffViewer, {
198
+ oldValue: oldValue,
199
+ newValue: newValue,
200
+ theme: "dark",
201
+ showUnchanged: false
202
+ })
203
+ }, index);
204
+ })
205
+ }), !hasHookChanges && hasChangedKeys && /*#__PURE__*/_jsx(View, {
206
+ style: styles.propsChangesContainer,
207
+ children: cause.changedKeys.filter(k => !k.includes("(ref only)")).slice(0, 5).map((key, index) => /*#__PURE__*/_jsx(View, {
208
+ style: styles.propChangeChip,
209
+ children: /*#__PURE__*/_jsx(Text, {
210
+ style: styles.propChangeText,
211
+ children: key
212
+ })
213
+ }, index))
214
+ })]
215
+ });
216
+ }
217
+
218
+ /**
219
+ * DetailsSection - Component identifiers, measurements, and stats
220
+ * Helps devs find the component in their codebase
221
+ */
222
+ function DetailsSection({
223
+ render,
224
+ rendersPerSec
225
+ }) {
226
+ const hasIdentifiers = render.testID || render.nativeID || render.accessibilityLabel;
227
+ const hasMeasurements = render.measurements;
228
+ return /*#__PURE__*/_jsxs(View, {
229
+ style: styles.detailsSection,
230
+ children: [hasIdentifiers && /*#__PURE__*/_jsxs(View, {
231
+ style: styles.detailsCard,
232
+ children: [/*#__PURE__*/_jsx(Text, {
233
+ style: styles.detailsCardTitle,
234
+ children: "Identifiers"
235
+ }), render.testID && /*#__PURE__*/_jsx(DetailRow, {
236
+ label: "testID",
237
+ value: render.testID
238
+ }), render.nativeID && /*#__PURE__*/_jsx(DetailRow, {
239
+ label: "nativeID",
240
+ value: render.nativeID
241
+ }), render.accessibilityLabel && /*#__PURE__*/_jsx(DetailRow, {
242
+ label: "a11y",
243
+ value: render.accessibilityLabel
244
+ })]
245
+ }), /*#__PURE__*/_jsxs(View, {
246
+ style: styles.statsRow,
247
+ children: [/*#__PURE__*/_jsxs(View, {
248
+ style: styles.statItem,
249
+ children: [/*#__PURE__*/_jsx(Text, {
250
+ style: [styles.statValue, {
251
+ color: render.color
252
+ }],
253
+ children: render.renderCount
254
+ }), /*#__PURE__*/_jsx(Text, {
255
+ style: styles.statLabel,
256
+ children: "renders"
257
+ })]
258
+ }), /*#__PURE__*/_jsx(View, {
259
+ style: styles.statDivider
260
+ }), /*#__PURE__*/_jsxs(View, {
261
+ style: styles.statItem,
262
+ children: [/*#__PURE__*/_jsx(Text, {
263
+ style: styles.statValue,
264
+ children: rendersPerSec
265
+ }), /*#__PURE__*/_jsx(Text, {
266
+ style: styles.statLabel,
267
+ children: "/sec"
268
+ })]
269
+ }), /*#__PURE__*/_jsx(View, {
270
+ style: styles.statDivider
271
+ }), /*#__PURE__*/_jsxs(View, {
272
+ style: styles.statItem,
273
+ children: [/*#__PURE__*/_jsx(Text, {
274
+ style: styles.statValue,
275
+ children: render.nativeTag
276
+ }), /*#__PURE__*/_jsx(Text, {
277
+ style: styles.statLabel,
278
+ children: "tag"
279
+ })]
280
+ })]
281
+ }), hasMeasurements && /*#__PURE__*/_jsxs(View, {
282
+ style: styles.measurementsRow,
283
+ children: [/*#__PURE__*/_jsxs(View, {
284
+ style: styles.measurementItem,
285
+ children: [/*#__PURE__*/_jsx(Text, {
286
+ style: styles.measurementLabel,
287
+ children: "x"
288
+ }), /*#__PURE__*/_jsx(Text, {
289
+ style: styles.measurementValue,
290
+ children: Math.round(render.measurements.x)
291
+ })]
292
+ }), /*#__PURE__*/_jsxs(View, {
293
+ style: styles.measurementItem,
294
+ children: [/*#__PURE__*/_jsx(Text, {
295
+ style: styles.measurementLabel,
296
+ children: "y"
297
+ }), /*#__PURE__*/_jsx(Text, {
298
+ style: styles.measurementValue,
299
+ children: Math.round(render.measurements.y)
300
+ })]
301
+ }), /*#__PURE__*/_jsxs(View, {
302
+ style: styles.measurementItem,
303
+ children: [/*#__PURE__*/_jsx(Text, {
304
+ style: styles.measurementLabel,
305
+ children: "w"
306
+ }), /*#__PURE__*/_jsx(Text, {
307
+ style: styles.measurementValue,
308
+ children: Math.round(render.measurements.width)
309
+ })]
310
+ }), /*#__PURE__*/_jsxs(View, {
311
+ style: styles.measurementItem,
312
+ children: [/*#__PURE__*/_jsx(Text, {
313
+ style: styles.measurementLabel,
314
+ children: "h"
315
+ }), /*#__PURE__*/_jsx(Text, {
316
+ style: styles.measurementValue,
317
+ children: Math.round(render.measurements.height)
318
+ })]
319
+ })]
320
+ })]
321
+ });
322
+ }
323
+
324
+ /**
325
+ * DetailRow - Single row for identifier display
326
+ */
327
+ function DetailRow({
328
+ label,
329
+ value
330
+ }) {
331
+ return /*#__PURE__*/_jsxs(View, {
332
+ style: styles.detailRow,
333
+ children: [/*#__PURE__*/_jsx(Text, {
334
+ style: styles.detailLabel,
335
+ children: label
336
+ }), /*#__PURE__*/_jsx(Text, {
337
+ style: styles.detailValue,
338
+ numberOfLines: 1,
339
+ children: value
340
+ })]
341
+ });
342
+ }
343
+
344
+ /**
345
+ * QuickActionsSection - Quick filter actions for the component
346
+ * Users select a filter option, then use action buttons to include/exclude
347
+ */
348
+ function QuickActionsSection({
349
+ render,
350
+ onAddFilter
351
+ }) {
352
+ const [selectedIndex, setSelectedIndex] = useState(null);
353
+
354
+ // Build list of available filter options (most specific to most general)
355
+ const filterOptions = useMemo(() => {
356
+ const options = [];
357
+
358
+ // Most specific first
359
+ if (render.nativeID) {
360
+ options.push({
361
+ type: "nativeID",
362
+ value: render.nativeID,
363
+ label: "nativeID"
364
+ });
365
+ }
366
+ if (render.testID) {
367
+ options.push({
368
+ type: "testID",
369
+ value: render.testID,
370
+ label: "testID"
371
+ });
372
+ }
373
+ if (render.accessibilityLabel) {
374
+ options.push({
375
+ type: "accessibilityLabel",
376
+ value: render.accessibilityLabel,
377
+ label: "a11y"
378
+ });
379
+ }
380
+ if (render.componentName) {
381
+ options.push({
382
+ type: "component",
383
+ value: render.componentName,
384
+ label: "component"
385
+ });
386
+ }
387
+ // Most general last (always available)
388
+ options.push({
389
+ type: "viewType",
390
+ value: render.viewType,
391
+ label: "viewType"
392
+ });
393
+ return options;
394
+ }, [render]);
395
+ const selectedOption = selectedIndex !== null ? filterOptions[selectedIndex] : null;
396
+ const handleSelectOption = useCallback(index => {
397
+ setSelectedIndex(prev => prev === index ? null : index);
398
+ }, []);
399
+ const handleInclude = useCallback(() => {
400
+ if (selectedOption) {
401
+ onAddFilter({
402
+ type: selectedOption.type,
403
+ value: selectedOption.value
404
+ }, "include");
405
+ setSelectedIndex(null);
406
+ }
407
+ }, [selectedOption, onAddFilter]);
408
+ const handleExclude = useCallback(() => {
409
+ if (selectedOption) {
410
+ onAddFilter({
411
+ type: selectedOption.type,
412
+ value: selectedOption.value
413
+ }, "exclude");
414
+ setSelectedIndex(null);
415
+ }
416
+ }, [selectedOption, onAddFilter]);
417
+ return /*#__PURE__*/_jsxs(View, {
418
+ style: styles.quickActionsSection,
419
+ children: [/*#__PURE__*/_jsx(Text, {
420
+ style: styles.quickActionsTitle,
421
+ children: "Quick Filters"
422
+ }), /*#__PURE__*/_jsx(View, {
423
+ style: styles.filterOptionsList,
424
+ children: filterOptions.map((option, index) => /*#__PURE__*/_jsx(FilterOptionCard, {
425
+ label: option.label,
426
+ value: option.value,
427
+ isSelected: selectedIndex === index,
428
+ onSelect: () => handleSelectOption(index)
429
+ }, option.type))
430
+ }), /*#__PURE__*/_jsxs(View, {
431
+ style: styles.filterActionButtons,
432
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
433
+ style: [styles.filterActionButton, styles.filterActionButtonInclude, !selectedOption && styles.filterActionButtonDisabled],
434
+ onPress: handleInclude,
435
+ disabled: !selectedOption,
436
+ activeOpacity: 0.7,
437
+ children: [/*#__PURE__*/_jsx(PlusIcon, {
438
+ size: 14,
439
+ color: selectedOption ? buoyColors.success : buoyColors.textMuted
440
+ }), /*#__PURE__*/_jsx(Text, {
441
+ style: [styles.filterActionButtonText, {
442
+ color: selectedOption ? buoyColors.success : buoyColors.textMuted
443
+ }],
444
+ children: "Only Show This"
445
+ })]
446
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
447
+ style: [styles.filterActionButton, styles.filterActionButtonExclude, !selectedOption && styles.filterActionButtonDisabled],
448
+ onPress: handleExclude,
449
+ disabled: !selectedOption,
450
+ activeOpacity: 0.7,
451
+ children: [/*#__PURE__*/_jsx(MinusIcon, {
452
+ size: 14,
453
+ color: selectedOption ? buoyColors.error : buoyColors.textMuted
454
+ }), /*#__PURE__*/_jsx(Text, {
455
+ style: [styles.filterActionButtonText, {
456
+ color: selectedOption ? buoyColors.error : buoyColors.textMuted
457
+ }],
458
+ children: "Hide This"
459
+ })]
460
+ })]
461
+ })]
462
+ });
463
+ }
464
+
465
+ /**
466
+ * FilterOptionCard - A selectable filter option card
467
+ */
468
+ function FilterOptionCard({
469
+ label,
470
+ value,
471
+ isSelected,
472
+ onSelect
473
+ }) {
474
+ return /*#__PURE__*/_jsxs(TouchableOpacity, {
475
+ style: [styles.filterOptionCard, isSelected && styles.filterOptionCardSelected],
476
+ onPress: onSelect,
477
+ activeOpacity: 0.7,
478
+ children: [/*#__PURE__*/_jsx(Text, {
479
+ style: [styles.filterOptionLabel, isSelected && styles.filterOptionLabelSelected],
480
+ children: label
481
+ }), /*#__PURE__*/_jsx(Text, {
482
+ style: [styles.filterOptionValue, isSelected && styles.filterOptionValueSelected],
483
+ numberOfLines: 1,
484
+ children: value
485
+ })]
486
+ });
487
+ }
488
+ const styles = StyleSheet.create({
489
+ container: {
490
+ flex: 1,
491
+ backgroundColor: buoyColors.base
492
+ },
493
+ // Header
494
+ header: {
495
+ flexDirection: "row",
496
+ alignItems: "center",
497
+ justifyContent: "center",
498
+ gap: 12,
499
+ padding: 16,
500
+ paddingBottom: 0
501
+ },
502
+ // ScrollView
503
+ scrollView: {
504
+ flex: 1
505
+ },
506
+ scrollContent: {
507
+ padding: 16,
508
+ paddingTop: 12,
509
+ paddingBottom: 80,
510
+ gap: 12
511
+ },
512
+ headerLeft: {
513
+ flex: 1,
514
+ flexDirection: "row",
515
+ alignItems: "center",
516
+ justifyContent: "center",
517
+ gap: 10
518
+ },
519
+ componentName: {
520
+ fontSize: 18,
521
+ fontWeight: "700",
522
+ color: buoyColors.text,
523
+ flexShrink: 1
524
+ },
525
+ nativeTypeBadge: {
526
+ backgroundColor: buoyColors.input,
527
+ paddingVertical: 3,
528
+ paddingHorizontal: 8,
529
+ borderRadius: 4
530
+ },
531
+ nativeTypeText: {
532
+ fontSize: 11,
533
+ fontWeight: "600",
534
+ color: buoyColors.textMuted,
535
+ fontFamily: "monospace"
536
+ },
537
+ // Answer Card
538
+ answerCard: {
539
+ backgroundColor: buoyColors.card,
540
+ borderRadius: 8,
541
+ borderWidth: 1,
542
+ borderColor: buoyColors.border,
543
+ padding: 12,
544
+ gap: 6
545
+ },
546
+ causeBadgeRow: {
547
+ flexDirection: "row",
548
+ alignItems: "center",
549
+ gap: 8
550
+ },
551
+ causeBadgeLarge: {
552
+ paddingVertical: 4,
553
+ paddingHorizontal: 10,
554
+ borderRadius: 4
555
+ },
556
+ causeBadgeLargeText: {
557
+ fontSize: 11,
558
+ fontWeight: "700",
559
+ letterSpacing: 0.5
560
+ },
561
+ causeLabel: {
562
+ fontSize: 12,
563
+ color: buoyColors.textSecondary,
564
+ fontWeight: "500"
565
+ },
566
+ hookChangesContainer: {
567
+ gap: 6
568
+ },
569
+ hookDiffContainer: {
570
+ backgroundColor: buoyColors.input,
571
+ borderRadius: 6,
572
+ overflow: "hidden"
573
+ },
574
+ propsChangesContainer: {
575
+ flexDirection: "row",
576
+ flexWrap: "wrap",
577
+ gap: 4
578
+ },
579
+ propChangeChip: {
580
+ backgroundColor: buoyColors.input,
581
+ paddingVertical: 2,
582
+ paddingHorizontal: 6,
583
+ borderRadius: 4
584
+ },
585
+ propChangeText: {
586
+ fontSize: 11,
587
+ color: buoyColors.text,
588
+ fontFamily: "monospace"
589
+ },
590
+ // Details Section
591
+ detailsSection: {
592
+ gap: 10
593
+ },
594
+ detailsCard: {
595
+ backgroundColor: buoyColors.card,
596
+ borderRadius: 8,
597
+ borderWidth: 1,
598
+ borderColor: buoyColors.border,
599
+ padding: 12,
600
+ gap: 6
601
+ },
602
+ detailsCardTitle: {
603
+ fontSize: 10,
604
+ fontWeight: "600",
605
+ color: buoyColors.textMuted,
606
+ letterSpacing: 0.5,
607
+ textTransform: "uppercase",
608
+ marginBottom: 4
609
+ },
610
+ detailRow: {
611
+ flexDirection: "row",
612
+ alignItems: "center",
613
+ gap: 8
614
+ },
615
+ detailLabel: {
616
+ fontSize: 11,
617
+ fontWeight: "600",
618
+ color: buoyColors.textSecondary,
619
+ minWidth: 55
620
+ },
621
+ detailValue: {
622
+ fontSize: 12,
623
+ color: buoyColors.text,
624
+ fontFamily: "monospace",
625
+ flex: 1
626
+ },
627
+ // Stats Row
628
+ statsRow: {
629
+ flexDirection: "row",
630
+ alignItems: "center",
631
+ justifyContent: "center",
632
+ backgroundColor: buoyColors.card,
633
+ borderRadius: 8,
634
+ borderWidth: 1,
635
+ borderColor: buoyColors.border,
636
+ paddingVertical: 10,
637
+ paddingHorizontal: 16
638
+ },
639
+ statItem: {
640
+ flex: 1,
641
+ alignItems: "center"
642
+ },
643
+ statValue: {
644
+ fontSize: 16,
645
+ fontWeight: "700",
646
+ color: buoyColors.text,
647
+ fontFamily: "monospace"
648
+ },
649
+ statLabel: {
650
+ fontSize: 10,
651
+ color: buoyColors.textMuted,
652
+ marginTop: 2
653
+ },
654
+ statDivider: {
655
+ width: 1,
656
+ height: 24,
657
+ backgroundColor: buoyColors.border,
658
+ marginHorizontal: 12
659
+ },
660
+ // Measurements Row
661
+ measurementsRow: {
662
+ flexDirection: "row",
663
+ alignItems: "center",
664
+ justifyContent: "space-around",
665
+ backgroundColor: buoyColors.card,
666
+ borderRadius: 8,
667
+ borderWidth: 1,
668
+ borderColor: buoyColors.border,
669
+ paddingVertical: 8,
670
+ paddingHorizontal: 12
671
+ },
672
+ measurementItem: {
673
+ flexDirection: "row",
674
+ alignItems: "baseline",
675
+ gap: 4
676
+ },
677
+ measurementLabel: {
678
+ fontSize: 10,
679
+ fontWeight: "600",
680
+ color: buoyColors.textMuted,
681
+ textTransform: "uppercase"
682
+ },
683
+ measurementValue: {
684
+ fontSize: 12,
685
+ fontWeight: "600",
686
+ color: buoyColors.text,
687
+ fontFamily: "monospace"
688
+ },
689
+ // Quick Actions Section
690
+ quickActionsSection: {
691
+ backgroundColor: buoyColors.card,
692
+ borderRadius: 8,
693
+ borderWidth: 1,
694
+ borderColor: buoyColors.border,
695
+ padding: 12,
696
+ gap: 10
697
+ },
698
+ quickActionsTitle: {
699
+ fontSize: 10,
700
+ fontWeight: "600",
701
+ color: buoyColors.textMuted,
702
+ letterSpacing: 0.5,
703
+ textTransform: "uppercase",
704
+ marginBottom: 2
705
+ },
706
+ filterOptionsList: {
707
+ gap: 6
708
+ },
709
+ filterOptionCard: {
710
+ flexDirection: "row",
711
+ alignItems: "center",
712
+ backgroundColor: buoyColors.input,
713
+ borderRadius: 6,
714
+ paddingVertical: 10,
715
+ paddingHorizontal: 12,
716
+ borderWidth: 1,
717
+ borderColor: "transparent",
718
+ gap: 10
719
+ },
720
+ filterOptionCardSelected: {
721
+ borderColor: buoyColors.success,
722
+ backgroundColor: buoyColors.success + "15"
723
+ },
724
+ filterOptionLabel: {
725
+ fontSize: 10,
726
+ fontWeight: "600",
727
+ color: buoyColors.textMuted,
728
+ minWidth: 70
729
+ },
730
+ filterOptionLabelSelected: {
731
+ color: buoyColors.success
732
+ },
733
+ filterOptionValue: {
734
+ fontSize: 12,
735
+ color: buoyColors.text,
736
+ fontFamily: "monospace",
737
+ flex: 1
738
+ },
739
+ filterOptionValueSelected: {
740
+ color: buoyColors.text
741
+ },
742
+ filterActionButtons: {
743
+ flexDirection: "row",
744
+ gap: 8,
745
+ marginTop: 12
746
+ },
747
+ filterActionButton: {
748
+ flexDirection: "row",
749
+ alignItems: "center",
750
+ justifyContent: "center",
751
+ gap: 4,
752
+ paddingVertical: 8,
753
+ paddingHorizontal: 10,
754
+ borderRadius: 6,
755
+ borderWidth: 1
756
+ },
757
+ filterActionButtonInclude: {
758
+ backgroundColor: buoyColors.success + "15",
759
+ borderColor: buoyColors.success + "40"
760
+ },
761
+ filterActionButtonExclude: {
762
+ backgroundColor: buoyColors.error + "15",
763
+ borderColor: buoyColors.error + "40"
764
+ },
765
+ filterActionButtonDisabled: {
766
+ backgroundColor: buoyColors.input,
767
+ borderColor: buoyColors.border,
768
+ opacity: 0.5
769
+ },
770
+ filterActionButtonText: {
771
+ fontSize: 11,
772
+ fontWeight: "600"
773
+ },
774
+ // Locked Banner
775
+ lockedBanner: {
776
+ flexDirection: "row",
777
+ alignItems: "center",
778
+ justifyContent: "center",
779
+ backgroundColor: buoyColors.card,
780
+ borderRadius: 8,
781
+ borderWidth: 1,
782
+ borderColor: buoyColors.warning + "40",
783
+ paddingVertical: 12,
784
+ paddingHorizontal: 16,
785
+ gap: 8
786
+ },
787
+ lockedBannerText: {
788
+ color: buoyColors.warning,
789
+ fontSize: 13,
790
+ fontWeight: "600"
791
+ },
792
+ lockedBannerSubtext: {
793
+ color: buoyColors.textMuted,
794
+ fontSize: 12
795
+ }
796
+ });
797
+ export default RenderDetailView;