@buoy-gg/highlight-updates 2.1.9 → 2.1.11

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 (65) 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 +591 -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 +303 -1
  7. package/lib/commonjs/highlight-updates/components/RenderCauseBadge.js +500 -1
  8. package/lib/commonjs/highlight-updates/components/RenderDetailView.js +830 -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/StatsDisplay.js +70 -1
  12. package/lib/commonjs/highlight-updates/components/index.js +97 -1
  13. package/lib/commonjs/highlight-updates/utils/HighlightUpdatesController.js +1435 -1
  14. package/lib/commonjs/highlight-updates/utils/PerformanceLogger.js +359 -1
  15. package/lib/commonjs/highlight-updates/utils/ProfilerInterceptor.js +371 -1
  16. package/lib/commonjs/highlight-updates/utils/RenderCauseDetector.js +1828 -1
  17. package/lib/commonjs/highlight-updates/utils/RenderTracker.js +903 -1
  18. package/lib/commonjs/highlight-updates/utils/ViewTypeMapper.js +264 -1
  19. package/lib/commonjs/highlight-updates/utils/renderExportFormatter.js +58 -1
  20. package/lib/commonjs/index.js +311 -1
  21. package/lib/commonjs/preset.js +278 -1
  22. package/lib/module/highlight-updates/HighlightUpdatesOverlay.js +278 -1
  23. package/lib/module/highlight-updates/components/HighlightFilterView.js +1365 -1
  24. package/lib/module/highlight-updates/components/HighlightUpdatesModal.js +585 -1
  25. package/lib/module/highlight-updates/components/IdentifierBadge.js +259 -1
  26. package/lib/module/highlight-updates/components/IsolatedRenderList.js +174 -1
  27. package/lib/module/highlight-updates/components/ModalHeaderContent.js +298 -1
  28. package/lib/module/highlight-updates/components/RenderCauseBadge.js +491 -1
  29. package/lib/module/highlight-updates/components/RenderDetailView.js +826 -1
  30. package/lib/module/highlight-updates/components/RenderHistoryViewer.js +888 -1
  31. package/lib/module/highlight-updates/components/RenderListItem.js +215 -1
  32. package/lib/module/highlight-updates/components/StatsDisplay.js +67 -1
  33. package/lib/module/highlight-updates/components/index.js +16 -1
  34. package/lib/module/highlight-updates/utils/HighlightUpdatesController.js +1431 -1
  35. package/lib/module/highlight-updates/utils/PerformanceLogger.js +353 -1
  36. package/lib/module/highlight-updates/utils/ProfilerInterceptor.js +358 -1
  37. package/lib/module/highlight-updates/utils/RenderCauseDetector.js +1818 -1
  38. package/lib/module/highlight-updates/utils/RenderTracker.js +900 -1
  39. package/lib/module/highlight-updates/utils/ViewTypeMapper.js +255 -1
  40. package/lib/module/highlight-updates/utils/renderExportFormatter.js +54 -1
  41. package/lib/module/index.js +71 -1
  42. package/lib/module/preset.js +272 -1
  43. package/lib/typescript/highlight-updates/HighlightUpdatesOverlay.d.ts.map +1 -0
  44. package/lib/typescript/highlight-updates/components/HighlightFilterView.d.ts.map +1 -0
  45. package/lib/typescript/highlight-updates/components/HighlightUpdatesModal.d.ts.map +1 -0
  46. package/lib/typescript/highlight-updates/components/IdentifierBadge.d.ts.map +1 -0
  47. package/lib/typescript/highlight-updates/components/IsolatedRenderList.d.ts.map +1 -0
  48. package/lib/typescript/highlight-updates/components/ModalHeaderContent.d.ts.map +1 -0
  49. package/lib/typescript/highlight-updates/components/RenderCauseBadge.d.ts.map +1 -0
  50. package/lib/typescript/highlight-updates/components/RenderDetailView.d.ts.map +1 -0
  51. package/lib/typescript/highlight-updates/components/RenderHistoryViewer.d.ts.map +1 -0
  52. package/lib/typescript/highlight-updates/components/RenderListItem.d.ts.map +1 -0
  53. package/lib/typescript/highlight-updates/components/StatsDisplay.d.ts.map +1 -0
  54. package/lib/typescript/highlight-updates/components/index.d.ts.map +1 -0
  55. package/lib/typescript/highlight-updates/utils/HighlightUpdatesController.d.ts.map +1 -0
  56. package/lib/typescript/highlight-updates/utils/PerformanceLogger.d.ts.map +1 -0
  57. package/lib/typescript/highlight-updates/utils/ProfilerInterceptor.d.ts.map +1 -0
  58. package/lib/typescript/highlight-updates/utils/RenderCauseDetector.d.ts.map +1 -0
  59. package/lib/typescript/highlight-updates/utils/RenderTracker.d.ts.map +1 -0
  60. package/lib/typescript/highlight-updates/utils/ViewTypeMapper.d.ts.map +1 -0
  61. package/lib/typescript/highlight-updates/utils/renderExportFormatter.d.ts.map +1 -0
  62. package/lib/typescript/index.d.ts.map +1 -0
  63. package/lib/typescript/preset.d.ts.map +1 -0
  64. package/package.json +16 -16
  65. package/LICENSE +0 -58
@@ -1 +1,888 @@
1
- "use strict";import React,{useState,useCallback,useMemo}from"react";import{View,Text,TouchableOpacity,ScrollView,StyleSheet}from"react-native";import{Clock,GitBranch,Database,Lock,formatRelativeTime,SectionHeader,EventStepperFooter,buoyColors}from"@buoy-gg/shared-ui";const FREE_TIER_EVENT_LIMIT=3;import{TreeDiffViewer}from"@buoy-gg/shared-ui/dataViewer";import{EnhancedCauseDisplay,CAUSE_CONFIG}from"./RenderCauseBadge";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";function formatTimeWithMs(e){const t=new Date(e);return t.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"})+`.${t.getMilliseconds().toString().padStart(3,"0")}`}export function RenderHistoryViewer({render:e,disableInternalFooter:t=!1,selectedEventIndex:o,onEventIndexChange:r,isPro:s=!1}){const[i,n]=useState(0),l=o??i,a=r??n,[d,c]=useState("current"),y=useMemo(()=>e.renderHistory&&0!==e.renderHistory.length?[...e.renderHistory].sort((e,t)=>e.timestamp-t.timestamp):[],[e.renderHistory]),u=useMemo(()=>s?y:y.slice(-3),[y,s]),x=useMemo(()=>s?0:Math.max(0,y.length-3),[y.length,s]),f=u.length,g=u[l],p=l>0?u[l-1]:null,b=useCallback(()=>{l>0&&a(l-1)},[l,a]),m=useCallback(()=>{l<f-1&&a(l+1)},[l,f,a]);return 0===f?_jsxs(View,{style:styles.emptyContainer,children:[_jsx(Clock,{size:32,color:buoyColors.textMuted}),_jsx(Text,{style:styles.emptyTitle,children:"No Render History"}),_jsx(Text,{style:styles.emptyText,children:"Enable render history tracking in settings to see render events here."})]}):_jsxs(_Fragment,{children:[_jsxs(View,{style:[styles.container,{paddingBottom:!t&&f>1?80:0}],children:[x>0&&_jsxs(View,{style:styles.lockedBanner,children:[_jsx(Lock,{size:14,color:buoyColors.warning}),_jsxs(Text,{style:styles.lockedBannerText,children:[x," older ",1===x?"render":"renders"," locked"]}),_jsx(Text,{style:styles.lockedBannerSubtext,children:"Upgrade to Pro"})]}),_jsxs(View,{style:styles.viewToggleContainer,children:[_jsxs(TouchableOpacity,{style:[styles.viewToggleCard,"current"===d&&styles.viewToggleCardActive],onPress:()=>c("current"),activeOpacity:.8,children:[_jsxs(View,{style:styles.viewToggleContent,children:[_jsx(Database,{size:16,color:"current"===d?buoyColors.primary:buoyColors.textSecondary}),_jsx(Text,{style:[styles.viewToggleLabel,"current"===d&&styles.viewToggleLabelActive],children:"CURRENT STATE"})]}),_jsxs(Text,{style:[styles.viewToggleDescription,"current"===d&&{color:buoyColors.text}],children:["View render #",g?.renderNumber??0," details"]})]}),_jsxs(View,{style:[styles.viewToggleCard,styles.viewToggleCardDisabled],children:[_jsxs(View,{style:styles.viewToggleContent,children:[_jsx(GitBranch,{size:16,color:buoyColors.textMuted}),_jsx(Text,{style:[styles.viewToggleLabel,styles.viewToggleLabelDisabled],children:"DIFF VIEW"}),_jsx(View,{style:styles.todoBadge,children:_jsx(Text,{style:styles.todoBadgeText,children:"TODO"})})]}),_jsx(Text,{style:[styles.viewToggleDescription,{color:buoyColors.textMuted}],children:"Compare renders side by side (coming soon)"})]})]}),_jsx(ScrollView,{style:styles.contentScroll,contentContainerStyle:styles.contentContainer,showsVerticalScrollIndicator:!1,children:"current"===d?_jsx(CurrentStateView,{event:g,render:e}):_jsx(DiffView,{previousEvent:p,currentEvent:g,render:e})})]}),!t&&_jsx(EventStepperFooter,{currentIndex:l,totalItems:f,onPrevious:b,onNext:m,itemLabel:"Render",subtitle:formatRelativeTime(new Date(g?.timestamp??Date.now()))})]})}function CurrentStateView({event:e,render:t}){if(!e)return _jsx(View,{style:styles.noEventContainer,children:_jsx(Text,{style:styles.noEventText,children:"No event selected"})});const o=CAUSE_CONFIG[e.cause.type];return _jsxs(View,{style:styles.currentStateContainer,children:[_jsxs(View,{style:styles.eventHeader,children:[_jsxs(View,{style:styles.eventHeaderLeft,children:[_jsxs(Text,{style:styles.eventTitle,children:["Render #",e.renderNumber]}),_jsx(Text,{style:styles.eventTime,children:formatTimeWithMs(e.timestamp)})]}),_jsxs(View,{style:[styles.eventBadge,{backgroundColor:t.color+"30"}],children:[_jsx(View,{style:[styles.eventBadgeDot,{backgroundColor:t.color}]}),_jsx(Text,{style:[styles.eventBadgeText,{color:t.color}],children:e.cause.type.toUpperCase()})]})]}),_jsxs(View,{style:styles.section,children:[_jsxs(SectionHeader,{children:[_jsx(SectionHeader.Icon,{icon:Clock,color:o.color,size:12}),_jsx(SectionHeader.Title,{children:"WHY DID THIS RENDER?"})]}),_jsx(View,{style:styles.sectionContent,children:_jsx(EnhancedCauseDisplay,{cause:e.cause,nativeType:t.viewType})})]}),e.capturedProps&&Object.keys(e.capturedProps).length>0&&_jsxs(View,{style:styles.section,children:[_jsxs(SectionHeader,{children:[_jsx(SectionHeader.Icon,{icon:Database,color:buoyColors.primary,size:12}),_jsx(SectionHeader.Title,{children:"CAPTURED PROPS"})]}),_jsx(View,{style:styles.sectionContent,children:_jsx(ScrollView,{horizontal:!0,showsHorizontalScrollIndicator:!1,style:styles.propsScrollView,children:_jsx(Text,{style:styles.propsJson,children:JSON.stringify(e.capturedProps,null,2)})})})]})]})}function DiffView({previousEvent:e,currentEvent:t,render:o}){const[r,s]=useState("props");if(!e||!t)return _jsx(View,{style:styles.noEventContainer,children:_jsx(Text,{style:styles.noEventText,children:"Select an event with a previous event to compare"})});const i=CAUSE_CONFIG[e.cause.type],n=CAUSE_CONFIG[t.cause.type],l=e.capturedProps&&t.capturedProps,a=e.capturedState&&t.capturedState;return _jsxs(View,{style:styles.diffContainer,children:[_jsxs(View,{style:styles.compareBar,children:[_jsxs(View,{style:styles.compareSide,children:[_jsxs(View,{style:styles.compareLabelRow,children:[_jsx(Text,{style:[styles.compareLabel,{color:buoyColors.warning}],children:"PREV"}),_jsx(View,{style:[styles.compareActionBadge,{backgroundColor:`${i.color}20`}],children:_jsx(Text,{style:[styles.compareActionText,{color:i.color}],children:e.cause.type.toUpperCase()})})]}),_jsxs(View,{style:styles.compareMeta,children:[_jsxs(Text,{style:styles.compareIndex,children:["#",e.renderNumber]}),_jsx(Text,{style:styles.compareTime,children:formatTimeWithMs(e.timestamp)})]})]}),_jsx(View,{style:styles.compareDivider,children:_jsx(Text,{style:styles.compareArrow,children:"→"})}),_jsxs(View,{style:styles.compareSide,children:[_jsxs(View,{style:styles.compareLabelRow,children:[_jsx(Text,{style:[styles.compareLabel,{color:buoyColors.success}],children:"CUR"}),_jsx(View,{style:[styles.compareActionBadge,{backgroundColor:`${n.color}20`}],children:_jsx(Text,{style:[styles.compareActionText,{color:n.color}],children:t.cause.type.toUpperCase()})})]}),_jsxs(View,{style:styles.compareMeta,children:[_jsxs(Text,{style:styles.compareIndex,children:["#",t.renderNumber]}),_jsx(Text,{style:styles.compareTime,children:formatTimeWithMs(t.timestamp)})]})]})]}),_jsxs(View,{style:styles.diffModeTabs,children:[_jsx(TouchableOpacity,{style:[styles.diffModeTab,"cause"===r&&styles.diffModeTabActive],onPress:()=>s("cause"),activeOpacity:.7,children:_jsx(Text,{style:[styles.diffModeTabText,"cause"===r&&styles.diffModeTabTextActive],children:"CAUSE"})}),_jsx(TouchableOpacity,{style:[styles.diffModeTab,"props"===r&&styles.diffModeTabActive,!l&&styles.diffModeTabDisabled],onPress:()=>l&&s("props"),activeOpacity:l?.7:1,children:_jsx(Text,{style:[styles.diffModeTabText,"props"===r&&styles.diffModeTabTextActive,!l&&styles.diffModeTabTextDisabled],children:"PROPS"})}),_jsx(TouchableOpacity,{style:[styles.diffModeTab,"state"===r&&styles.diffModeTabActive,!a&&styles.diffModeTabDisabled],onPress:()=>a&&s("state"),activeOpacity:a?.7:1,children:_jsx(Text,{style:[styles.diffModeTabText,"state"===r&&styles.diffModeTabTextActive,!a&&styles.diffModeTabTextDisabled],children:"STATE"})})]}),"cause"===r&&_jsx(View,{style:styles.diffSummary,children:_jsx(EnhancedCauseDisplay,{cause:t.cause,nativeType:o.viewType})}),"props"===r&&_jsx(View,{style:styles.treeDiffContainer,children:l?_jsx(TreeDiffViewer,{oldValue:e.capturedProps,newValue:t.capturedProps,theme:"dark",showUnchanged:!0}):_jsxs(View,{style:styles.noDiffData,children:[_jsx(Text,{style:styles.noDiffDataTitle,children:"Props Not Captured"}),_jsx(Text,{style:styles.noDiffDataText,children:'Enable "Capture Props on Render" in settings to see props diff.'})]})}),"state"===r&&_jsx(View,{style:styles.treeDiffContainer,children:a?_jsx(TreeDiffViewer,{oldValue:e.capturedState,newValue:t.capturedState,theme:"dark",showUnchanged:!0}):_jsxs(View,{style:styles.noDiffData,children:[_jsx(Text,{style:styles.noDiffDataTitle,children:"State Not Captured"}),_jsx(Text,{style:styles.noDiffDataText,children:'Enable "Capture State on Render" in settings to see state diff.'})]})})]})}export function RenderHistoryFooter({render:e,selectedEventIndex:t=0,onEventIndexChange:o=()=>{},isPro:r=!1}){const s=useMemo(()=>e.renderHistory&&0!==e.renderHistory.length?[...e.renderHistory].sort((e,t)=>e.timestamp-t.timestamp):[],[e.renderHistory]),i=useMemo(()=>r?s:s.slice(-3),[s,r]),n=i.length,l=i[t],a=useCallback(()=>{o(Math.max(0,t-1))},[t,o]),d=useCallback(()=>{o(Math.min(n-1,t+1))},[t,n,o]);return n<=1?null:_jsx(EventStepperFooter,{currentIndex:t,totalItems:n,onPrevious:a,onNext:d,itemLabel:"Render",subtitle:l?formatRelativeTime(new Date(l.timestamp)):void 0})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:buoyColors.base},emptyContainer:{flex:1,alignItems:"center",justifyContent:"center",padding:32,gap:12},emptyTitle:{fontSize:16,fontWeight:"600",color:buoyColors.text},emptyText:{fontSize:13,color:buoyColors.textSecondary,textAlign:"center",lineHeight:18},viewToggleContainer:{flexDirection:"row",padding:12,gap:8},viewToggleCard:{flex:1,backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:10,gap:4},viewToggleCardActive:{borderColor:buoyColors.primary,backgroundColor:buoyColors.primary+"10"},viewToggleCardDisabled:{opacity:.5},todoBadge:{backgroundColor:buoyColors.textMuted+"30",paddingHorizontal:6,paddingVertical:2,borderRadius:4,marginLeft:4},todoBadgeText:{fontSize:9,fontWeight:"700",color:buoyColors.textMuted,letterSpacing:.3},viewToggleContent:{flexDirection:"row",alignItems:"center",gap:6},viewToggleLabel:{fontSize:10,fontWeight:"700",color:buoyColors.textSecondary,letterSpacing:.5},viewToggleLabelActive:{color:buoyColors.primary},viewToggleLabelDisabled:{color:buoyColors.textMuted},viewToggleDescription:{fontSize:11,color:buoyColors.textMuted,marginTop:2},contentScroll:{flex:1},contentContainer:{padding:12,paddingTop:0},noEventContainer:{padding:24,alignItems:"center"},noEventText:{fontSize:13,color:buoyColors.textMuted},currentStateContainer:{gap:12},eventHeader:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:12},eventHeaderLeft:{gap:2},eventTitle:{fontSize:15,fontWeight:"700",color:buoyColors.text},eventTime:{fontSize:11,color:buoyColors.textMuted,fontFamily:"monospace"},eventBadge:{flexDirection:"row",alignItems:"center",paddingHorizontal:10,paddingVertical:4,borderRadius:12,gap:6},eventBadgeDot:{width:6,height:6,borderRadius:3},eventBadgeText:{fontSize:10,fontWeight:"700",fontFamily:"monospace"},section:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,overflow:"hidden"},sectionContent:{padding:12,paddingTop:8},causeContainer:{marginBottom:12},changedList:{marginBottom:12,paddingLeft:4},changedListTitle:{fontSize:11,fontWeight:"600",color:buoyColors.textSecondary,marginBottom:6},changedItem:{fontSize:12,color:buoyColors.text,fontFamily:"monospace",marginBottom:3,paddingLeft:8},propsScrollView:{maxHeight:150},propsJson:{fontSize:11,fontFamily:"monospace",color:buoyColors.text,backgroundColor:buoyColors.input,padding:10,borderRadius:6},diffContainer:{gap:12},compareBar:{flexDirection:"row",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:10},compareSide:{flex:1,gap:4},compareLabelRow:{flexDirection:"row",alignItems:"center",gap:8},compareLabel:{fontSize:10,fontWeight:"700",letterSpacing:.5},compareActionBadge:{paddingHorizontal:6,paddingVertical:2,borderRadius:4},compareActionText:{fontSize:9,fontWeight:"600"},compareMeta:{flexDirection:"row",alignItems:"center",gap:6},compareIndex:{fontSize:12,fontWeight:"600",color:buoyColors.text},compareTime:{fontSize:10,color:buoyColors.textMuted,fontFamily:"monospace"},compareDivider:{width:30,alignItems:"center",justifyContent:"center"},compareArrow:{fontSize:16,color:buoyColors.textMuted},diffSummary:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:12},diffSummaryTitle:{fontSize:11,fontWeight:"600",color:buoyColors.textSecondary,marginBottom:8},diffChanges:{gap:4},diffChangeItem:{flexDirection:"row",alignItems:"center",gap:6},diffChangeIcon:{fontSize:12,fontWeight:"700",color:buoyColors.warning,fontFamily:"monospace"},diffChangeKey:{fontSize:12,color:buoyColors.text,fontFamily:"monospace"},diffNoChanges:{fontSize:12,color:buoyColors.textMuted,fontStyle:"italic"},diffPlaceholder:{fontSize:12,color:buoyColors.textMuted,fontStyle:"italic",textAlign:"center",padding:16},diffModeTabs:{flexDirection:"row",backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,padding:4,gap:4},diffModeTab:{flex:1,paddingVertical:8,paddingHorizontal:12,borderRadius:6,alignItems:"center"},diffModeTabActive:{backgroundColor:buoyColors.primary+"20"},diffModeTabDisabled:{opacity:.4},diffModeTabText:{fontSize:10,fontWeight:"600",color:buoyColors.textSecondary,letterSpacing:.5},diffModeTabTextActive:{color:buoyColors.primary},diffModeTabTextDisabled:{color:buoyColors.textMuted},causeDiffContainer:{gap:8,marginBottom:12},causeDiffRow:{flexDirection:"row",alignItems:"center",gap:8},causeDiffLabel:{fontSize:11,fontWeight:"600",color:buoyColors.textSecondary,minWidth:80},causeDiffValue:{fontSize:12,color:buoyColors.text,fontFamily:"monospace",fontWeight:"500"},diffChangesTitle:{fontSize:11,fontWeight:"600",color:buoyColors.textSecondary,marginBottom:6},treeDiffContainer:{backgroundColor:buoyColors.card,borderRadius:8,borderWidth:1,borderColor:buoyColors.border,overflow:"hidden",minHeight:150},noDiffData:{padding:24,alignItems:"center",gap:8},noDiffDataTitle:{fontSize:13,fontWeight:"600",color:buoyColors.text},noDiffDataText:{fontSize:12,color:buoyColors.textMuted,textAlign:"center",lineHeight:18},lockedBanner:{flexDirection:"row",alignItems:"center",justifyContent:"center",paddingVertical:10,paddingHorizontal:16,marginHorizontal:12,marginTop:8,backgroundColor:buoyColors.warning+"15",borderRadius:8,borderWidth:1,borderColor:buoyColors.warning+"30",gap:8},lockedBannerText:{color:buoyColors.warning,fontSize:13,fontWeight:"500"},lockedBannerSubtext:{color:buoyColors.textMuted,fontSize:12}});export default RenderHistoryViewer;
1
+ "use strict";
2
+
3
+ /**
4
+ * RenderHistoryViewer
5
+ *
6
+ * Displays render history for a component with event stepping.
7
+ * Allows navigating through render events chronologically.
8
+ * Shows current state view or diff view between renders.
9
+ */
10
+
11
+ import React, { useState, useCallback, useMemo } from "react";
12
+ import { View, Text, TouchableOpacity, ScrollView, StyleSheet } from "react-native";
13
+ import { Clock, GitBranch, Database, Lock, formatRelativeTime, SectionHeader, EventStepperFooter, buoyColors } from "@buoy-gg/shared-ui";
14
+
15
+ // Free tier limit for render history events
16
+ const FREE_TIER_EVENT_LIMIT = 3;
17
+ import { TreeDiffViewer } from "@buoy-gg/shared-ui/dataViewer";
18
+ import { EnhancedCauseDisplay, CAUSE_CONFIG } from "./RenderCauseBadge";
19
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
20
+ /**
21
+ * Format timestamp with milliseconds
22
+ */
23
+ function formatTimeWithMs(timestamp) {
24
+ const date = new Date(timestamp);
25
+ return date.toLocaleTimeString([], {
26
+ hour: "2-digit",
27
+ minute: "2-digit",
28
+ second: "2-digit"
29
+ }) + `.${date.getMilliseconds().toString().padStart(3, "0")}`;
30
+ }
31
+
32
+ /**
33
+ * Main RenderHistoryViewer component
34
+ */
35
+ export function RenderHistoryViewer({
36
+ render,
37
+ disableInternalFooter = false,
38
+ selectedEventIndex: externalIndex,
39
+ onEventIndexChange: externalOnChange,
40
+ isPro = false
41
+ }) {
42
+ // Internal state for event index when not controlled externally
43
+ const [internalIndex, setInternalIndex] = useState(0);
44
+
45
+ // Use external or internal state
46
+ const selectedEventIndex = externalIndex ?? internalIndex;
47
+ const onEventIndexChange = externalOnChange ?? setInternalIndex;
48
+
49
+ // View mode: "current" shows selected event, "diff" shows comparison
50
+ const [activeView, setActiveView] = useState("current");
51
+
52
+ // Get all events sorted by timestamp (oldest first)
53
+ const allEvents = useMemo(() => {
54
+ if (!render.renderHistory || render.renderHistory.length === 0) {
55
+ return [];
56
+ }
57
+ return [...render.renderHistory].sort((a, b) => a.timestamp - b.timestamp);
58
+ }, [render.renderHistory]);
59
+
60
+ // Limit visible events for free tier (show most recent N events)
61
+ const events = useMemo(() => {
62
+ if (isPro) return allEvents;
63
+ // Take the most recent events (last N in the sorted list)
64
+ return allEvents.slice(-FREE_TIER_EVENT_LIMIT);
65
+ }, [allEvents, isPro]);
66
+
67
+ // Calculate how many events are locked
68
+ const lockedEventCount = useMemo(() => {
69
+ if (isPro) return 0;
70
+ return Math.max(0, allEvents.length - FREE_TIER_EVENT_LIMIT);
71
+ }, [allEvents.length, isPro]);
72
+ const totalEvents = events.length;
73
+ const currentEvent = events[selectedEventIndex];
74
+ const previousEvent = selectedEventIndex > 0 ? events[selectedEventIndex - 1] : null;
75
+
76
+ // Navigation handlers
77
+ const goToPrevious = useCallback(() => {
78
+ if (selectedEventIndex > 0) {
79
+ onEventIndexChange(selectedEventIndex - 1);
80
+ }
81
+ }, [selectedEventIndex, onEventIndexChange]);
82
+ const goToNext = useCallback(() => {
83
+ if (selectedEventIndex < totalEvents - 1) {
84
+ onEventIndexChange(selectedEventIndex + 1);
85
+ }
86
+ }, [selectedEventIndex, totalEvents, onEventIndexChange]);
87
+
88
+ // If no history, show empty state
89
+ if (totalEvents === 0) {
90
+ return /*#__PURE__*/_jsxs(View, {
91
+ style: styles.emptyContainer,
92
+ children: [/*#__PURE__*/_jsx(Clock, {
93
+ size: 32,
94
+ color: buoyColors.textMuted
95
+ }), /*#__PURE__*/_jsx(Text, {
96
+ style: styles.emptyTitle,
97
+ children: "No Render History"
98
+ }), /*#__PURE__*/_jsx(Text, {
99
+ style: styles.emptyText,
100
+ children: "Enable render history tracking in settings to see render events here."
101
+ })]
102
+ });
103
+ }
104
+ return /*#__PURE__*/_jsxs(_Fragment, {
105
+ children: [/*#__PURE__*/_jsxs(View, {
106
+ style: [styles.container, {
107
+ paddingBottom: !disableInternalFooter && totalEvents > 1 ? 80 : 0
108
+ }],
109
+ children: [lockedEventCount > 0 && /*#__PURE__*/_jsxs(View, {
110
+ style: styles.lockedBanner,
111
+ children: [/*#__PURE__*/_jsx(Lock, {
112
+ size: 14,
113
+ color: buoyColors.warning
114
+ }), /*#__PURE__*/_jsxs(Text, {
115
+ style: styles.lockedBannerText,
116
+ children: [lockedEventCount, " older ", lockedEventCount === 1 ? 'render' : 'renders', " locked"]
117
+ }), /*#__PURE__*/_jsx(Text, {
118
+ style: styles.lockedBannerSubtext,
119
+ children: "Upgrade to Pro"
120
+ })]
121
+ }), /*#__PURE__*/_jsxs(View, {
122
+ style: styles.viewToggleContainer,
123
+ children: [/*#__PURE__*/_jsxs(TouchableOpacity, {
124
+ style: [styles.viewToggleCard, activeView === "current" && styles.viewToggleCardActive],
125
+ onPress: () => setActiveView("current"),
126
+ activeOpacity: 0.8,
127
+ children: [/*#__PURE__*/_jsxs(View, {
128
+ style: styles.viewToggleContent,
129
+ children: [/*#__PURE__*/_jsx(Database, {
130
+ size: 16,
131
+ color: activeView === "current" ? buoyColors.primary : buoyColors.textSecondary
132
+ }), /*#__PURE__*/_jsx(Text, {
133
+ style: [styles.viewToggleLabel, activeView === "current" && styles.viewToggleLabelActive],
134
+ children: "CURRENT STATE"
135
+ })]
136
+ }), /*#__PURE__*/_jsxs(Text, {
137
+ style: [styles.viewToggleDescription, activeView === "current" && {
138
+ color: buoyColors.text
139
+ }],
140
+ children: ["View render #", currentEvent?.renderNumber ?? 0, " details"]
141
+ })]
142
+ }), /*#__PURE__*/_jsxs(View, {
143
+ style: [styles.viewToggleCard, styles.viewToggleCardDisabled],
144
+ children: [/*#__PURE__*/_jsxs(View, {
145
+ style: styles.viewToggleContent,
146
+ children: [/*#__PURE__*/_jsx(GitBranch, {
147
+ size: 16,
148
+ color: buoyColors.textMuted
149
+ }), /*#__PURE__*/_jsx(Text, {
150
+ style: [styles.viewToggleLabel, styles.viewToggleLabelDisabled],
151
+ children: "DIFF VIEW"
152
+ }), /*#__PURE__*/_jsx(View, {
153
+ style: styles.todoBadge,
154
+ children: /*#__PURE__*/_jsx(Text, {
155
+ style: styles.todoBadgeText,
156
+ children: "TODO"
157
+ })
158
+ })]
159
+ }), /*#__PURE__*/_jsx(Text, {
160
+ style: [styles.viewToggleDescription, {
161
+ color: buoyColors.textMuted
162
+ }],
163
+ children: "Compare renders side by side (coming soon)"
164
+ })]
165
+ })]
166
+ }), /*#__PURE__*/_jsx(ScrollView, {
167
+ style: styles.contentScroll,
168
+ contentContainerStyle: styles.contentContainer,
169
+ showsVerticalScrollIndicator: false,
170
+ children: activeView === "current" ? /*#__PURE__*/_jsx(CurrentStateView, {
171
+ event: currentEvent,
172
+ render: render
173
+ }) : /*#__PURE__*/_jsx(DiffView, {
174
+ previousEvent: previousEvent,
175
+ currentEvent: currentEvent,
176
+ render: render
177
+ })
178
+ })]
179
+ }), !disableInternalFooter && /*#__PURE__*/_jsx(EventStepperFooter, {
180
+ currentIndex: selectedEventIndex,
181
+ totalItems: totalEvents,
182
+ onPrevious: goToPrevious,
183
+ onNext: goToNext,
184
+ itemLabel: "Render",
185
+ subtitle: formatRelativeTime(new Date(currentEvent?.timestamp ?? Date.now()))
186
+ })]
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Current State View - shows details of the selected render event
192
+ */
193
+ function CurrentStateView({
194
+ event,
195
+ render
196
+ }) {
197
+ if (!event) {
198
+ return /*#__PURE__*/_jsx(View, {
199
+ style: styles.noEventContainer,
200
+ children: /*#__PURE__*/_jsx(Text, {
201
+ style: styles.noEventText,
202
+ children: "No event selected"
203
+ })
204
+ });
205
+ }
206
+ const causeConfig = CAUSE_CONFIG[event.cause.type];
207
+ return /*#__PURE__*/_jsxs(View, {
208
+ style: styles.currentStateContainer,
209
+ children: [/*#__PURE__*/_jsxs(View, {
210
+ style: styles.eventHeader,
211
+ children: [/*#__PURE__*/_jsxs(View, {
212
+ style: styles.eventHeaderLeft,
213
+ children: [/*#__PURE__*/_jsxs(Text, {
214
+ style: styles.eventTitle,
215
+ children: ["Render #", event.renderNumber]
216
+ }), /*#__PURE__*/_jsx(Text, {
217
+ style: styles.eventTime,
218
+ children: formatTimeWithMs(event.timestamp)
219
+ })]
220
+ }), /*#__PURE__*/_jsxs(View, {
221
+ style: [styles.eventBadge, {
222
+ backgroundColor: render.color + "30"
223
+ }],
224
+ children: [/*#__PURE__*/_jsx(View, {
225
+ style: [styles.eventBadgeDot, {
226
+ backgroundColor: render.color
227
+ }]
228
+ }), /*#__PURE__*/_jsx(Text, {
229
+ style: [styles.eventBadgeText, {
230
+ color: render.color
231
+ }],
232
+ children: event.cause.type.toUpperCase()
233
+ })]
234
+ })]
235
+ }), /*#__PURE__*/_jsxs(View, {
236
+ style: styles.section,
237
+ children: [/*#__PURE__*/_jsxs(SectionHeader, {
238
+ children: [/*#__PURE__*/_jsx(SectionHeader.Icon, {
239
+ icon: Clock,
240
+ color: causeConfig.color,
241
+ size: 12
242
+ }), /*#__PURE__*/_jsx(SectionHeader.Title, {
243
+ children: "WHY DID THIS RENDER?"
244
+ })]
245
+ }), /*#__PURE__*/_jsx(View, {
246
+ style: styles.sectionContent,
247
+ children: /*#__PURE__*/_jsx(EnhancedCauseDisplay, {
248
+ cause: event.cause,
249
+ nativeType: render.viewType
250
+ })
251
+ })]
252
+ }), event.capturedProps && Object.keys(event.capturedProps).length > 0 && /*#__PURE__*/_jsxs(View, {
253
+ style: styles.section,
254
+ children: [/*#__PURE__*/_jsxs(SectionHeader, {
255
+ children: [/*#__PURE__*/_jsx(SectionHeader.Icon, {
256
+ icon: Database,
257
+ color: buoyColors.primary,
258
+ size: 12
259
+ }), /*#__PURE__*/_jsx(SectionHeader.Title, {
260
+ children: "CAPTURED PROPS"
261
+ })]
262
+ }), /*#__PURE__*/_jsx(View, {
263
+ style: styles.sectionContent,
264
+ children: /*#__PURE__*/_jsx(ScrollView, {
265
+ horizontal: true,
266
+ showsHorizontalScrollIndicator: false,
267
+ style: styles.propsScrollView,
268
+ children: /*#__PURE__*/_jsx(Text, {
269
+ style: styles.propsJson,
270
+ children: JSON.stringify(event.capturedProps, null, 2)
271
+ })
272
+ })
273
+ })]
274
+ })]
275
+ });
276
+ }
277
+
278
+ /**
279
+ * Diff mode types
280
+ */
281
+
282
+ /**
283
+ * Diff View - shows comparison between two render events
284
+ */
285
+ function DiffView({
286
+ previousEvent,
287
+ currentEvent,
288
+ render
289
+ }) {
290
+ // Diff mode tab state
291
+ const [diffMode, setDiffMode] = useState("props");
292
+ if (!previousEvent || !currentEvent) {
293
+ return /*#__PURE__*/_jsx(View, {
294
+ style: styles.noEventContainer,
295
+ children: /*#__PURE__*/_jsx(Text, {
296
+ style: styles.noEventText,
297
+ children: "Select an event with a previous event to compare"
298
+ })
299
+ });
300
+ }
301
+ const prevCauseConfig = CAUSE_CONFIG[previousEvent.cause.type];
302
+ const currCauseConfig = CAUSE_CONFIG[currentEvent.cause.type];
303
+
304
+ // Check what data is available for diff
305
+ const hasPropsData = previousEvent.capturedProps && currentEvent.capturedProps;
306
+ const hasStateData = previousEvent.capturedState && currentEvent.capturedState;
307
+ return /*#__PURE__*/_jsxs(View, {
308
+ style: styles.diffContainer,
309
+ children: [/*#__PURE__*/_jsxs(View, {
310
+ style: styles.compareBar,
311
+ children: [/*#__PURE__*/_jsxs(View, {
312
+ style: styles.compareSide,
313
+ children: [/*#__PURE__*/_jsxs(View, {
314
+ style: styles.compareLabelRow,
315
+ children: [/*#__PURE__*/_jsx(Text, {
316
+ style: [styles.compareLabel, {
317
+ color: buoyColors.warning
318
+ }],
319
+ children: "PREV"
320
+ }), /*#__PURE__*/_jsx(View, {
321
+ style: [styles.compareActionBadge, {
322
+ backgroundColor: `${prevCauseConfig.color}20`
323
+ }],
324
+ children: /*#__PURE__*/_jsx(Text, {
325
+ style: [styles.compareActionText, {
326
+ color: prevCauseConfig.color
327
+ }],
328
+ children: previousEvent.cause.type.toUpperCase()
329
+ })
330
+ })]
331
+ }), /*#__PURE__*/_jsxs(View, {
332
+ style: styles.compareMeta,
333
+ children: [/*#__PURE__*/_jsxs(Text, {
334
+ style: styles.compareIndex,
335
+ children: ["#", previousEvent.renderNumber]
336
+ }), /*#__PURE__*/_jsx(Text, {
337
+ style: styles.compareTime,
338
+ children: formatTimeWithMs(previousEvent.timestamp)
339
+ })]
340
+ })]
341
+ }), /*#__PURE__*/_jsx(View, {
342
+ style: styles.compareDivider,
343
+ children: /*#__PURE__*/_jsx(Text, {
344
+ style: styles.compareArrow,
345
+ children: "\u2192"
346
+ })
347
+ }), /*#__PURE__*/_jsxs(View, {
348
+ style: styles.compareSide,
349
+ children: [/*#__PURE__*/_jsxs(View, {
350
+ style: styles.compareLabelRow,
351
+ children: [/*#__PURE__*/_jsx(Text, {
352
+ style: [styles.compareLabel, {
353
+ color: buoyColors.success
354
+ }],
355
+ children: "CUR"
356
+ }), /*#__PURE__*/_jsx(View, {
357
+ style: [styles.compareActionBadge, {
358
+ backgroundColor: `${currCauseConfig.color}20`
359
+ }],
360
+ children: /*#__PURE__*/_jsx(Text, {
361
+ style: [styles.compareActionText, {
362
+ color: currCauseConfig.color
363
+ }],
364
+ children: currentEvent.cause.type.toUpperCase()
365
+ })
366
+ })]
367
+ }), /*#__PURE__*/_jsxs(View, {
368
+ style: styles.compareMeta,
369
+ children: [/*#__PURE__*/_jsxs(Text, {
370
+ style: styles.compareIndex,
371
+ children: ["#", currentEvent.renderNumber]
372
+ }), /*#__PURE__*/_jsx(Text, {
373
+ style: styles.compareTime,
374
+ children: formatTimeWithMs(currentEvent.timestamp)
375
+ })]
376
+ })]
377
+ })]
378
+ }), /*#__PURE__*/_jsxs(View, {
379
+ style: styles.diffModeTabs,
380
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
381
+ style: [styles.diffModeTab, diffMode === "cause" && styles.diffModeTabActive],
382
+ onPress: () => setDiffMode("cause"),
383
+ activeOpacity: 0.7,
384
+ children: /*#__PURE__*/_jsx(Text, {
385
+ style: [styles.diffModeTabText, diffMode === "cause" && styles.diffModeTabTextActive],
386
+ children: "CAUSE"
387
+ })
388
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
389
+ style: [styles.diffModeTab, diffMode === "props" && styles.diffModeTabActive, !hasPropsData && styles.diffModeTabDisabled],
390
+ onPress: () => hasPropsData && setDiffMode("props"),
391
+ activeOpacity: hasPropsData ? 0.7 : 1,
392
+ children: /*#__PURE__*/_jsx(Text, {
393
+ style: [styles.diffModeTabText, diffMode === "props" && styles.diffModeTabTextActive, !hasPropsData && styles.diffModeTabTextDisabled],
394
+ children: "PROPS"
395
+ })
396
+ }), /*#__PURE__*/_jsx(TouchableOpacity, {
397
+ style: [styles.diffModeTab, diffMode === "state" && styles.diffModeTabActive, !hasStateData && styles.diffModeTabDisabled],
398
+ onPress: () => hasStateData && setDiffMode("state"),
399
+ activeOpacity: hasStateData ? 0.7 : 1,
400
+ children: /*#__PURE__*/_jsx(Text, {
401
+ style: [styles.diffModeTabText, diffMode === "state" && styles.diffModeTabTextActive, !hasStateData && styles.diffModeTabTextDisabled],
402
+ children: "STATE"
403
+ })
404
+ })]
405
+ }), diffMode === "cause" && /*#__PURE__*/_jsx(View, {
406
+ style: styles.diffSummary,
407
+ children: /*#__PURE__*/_jsx(EnhancedCauseDisplay, {
408
+ cause: currentEvent.cause,
409
+ nativeType: render.viewType
410
+ })
411
+ }), diffMode === "props" && /*#__PURE__*/_jsx(View, {
412
+ style: styles.treeDiffContainer,
413
+ children: hasPropsData ? /*#__PURE__*/_jsx(TreeDiffViewer, {
414
+ oldValue: previousEvent.capturedProps,
415
+ newValue: currentEvent.capturedProps,
416
+ theme: "dark",
417
+ showUnchanged: true
418
+ }) : /*#__PURE__*/_jsxs(View, {
419
+ style: styles.noDiffData,
420
+ children: [/*#__PURE__*/_jsx(Text, {
421
+ style: styles.noDiffDataTitle,
422
+ children: "Props Not Captured"
423
+ }), /*#__PURE__*/_jsx(Text, {
424
+ style: styles.noDiffDataText,
425
+ children: "Enable \"Capture Props on Render\" in settings to see props diff."
426
+ })]
427
+ })
428
+ }), diffMode === "state" && /*#__PURE__*/_jsx(View, {
429
+ style: styles.treeDiffContainer,
430
+ children: hasStateData ? /*#__PURE__*/_jsx(TreeDiffViewer, {
431
+ oldValue: previousEvent.capturedState,
432
+ newValue: currentEvent.capturedState,
433
+ theme: "dark",
434
+ showUnchanged: true
435
+ }) : /*#__PURE__*/_jsxs(View, {
436
+ style: styles.noDiffData,
437
+ children: [/*#__PURE__*/_jsx(Text, {
438
+ style: styles.noDiffDataTitle,
439
+ children: "State Not Captured"
440
+ }), /*#__PURE__*/_jsx(Text, {
441
+ style: styles.noDiffDataText,
442
+ children: "Enable \"Capture State on Render\" in settings to see state diff."
443
+ })]
444
+ })
445
+ })]
446
+ });
447
+ }
448
+
449
+ /**
450
+ * External footer component for modal integration
451
+ * Uses renderHistory for events (only available when history tracking is enabled)
452
+ */
453
+ export function RenderHistoryFooter({
454
+ render,
455
+ selectedEventIndex = 0,
456
+ onEventIndexChange = () => {},
457
+ isPro = false
458
+ }) {
459
+ // Get all events from render history (sorted by timestamp)
460
+ const allEvents = useMemo(() => {
461
+ if (!render.renderHistory || render.renderHistory.length === 0) {
462
+ return [];
463
+ }
464
+ return [...render.renderHistory].sort((a, b) => a.timestamp - b.timestamp);
465
+ }, [render.renderHistory]);
466
+
467
+ // Limit visible events for free tier (show most recent N events)
468
+ const events = useMemo(() => {
469
+ if (isPro) return allEvents;
470
+ return allEvents.slice(-FREE_TIER_EVENT_LIMIT);
471
+ }, [allEvents, isPro]);
472
+ const totalEvents = events.length;
473
+ const currentEvent = events[selectedEventIndex];
474
+ const goToPrevious = useCallback(() => {
475
+ onEventIndexChange(Math.max(0, selectedEventIndex - 1));
476
+ }, [selectedEventIndex, onEventIndexChange]);
477
+ const goToNext = useCallback(() => {
478
+ onEventIndexChange(Math.min(totalEvents - 1, selectedEventIndex + 1));
479
+ }, [selectedEventIndex, totalEvents, onEventIndexChange]);
480
+
481
+ // Don't render if no history events (history tracking might be disabled)
482
+ if (totalEvents <= 1) {
483
+ return null;
484
+ }
485
+ return /*#__PURE__*/_jsx(EventStepperFooter, {
486
+ currentIndex: selectedEventIndex,
487
+ totalItems: totalEvents,
488
+ onPrevious: goToPrevious,
489
+ onNext: goToNext,
490
+ itemLabel: "Render",
491
+ subtitle: currentEvent ? formatRelativeTime(new Date(currentEvent.timestamp)) : undefined
492
+ });
493
+ }
494
+ const styles = StyleSheet.create({
495
+ container: {
496
+ flex: 1,
497
+ backgroundColor: buoyColors.base
498
+ },
499
+ emptyContainer: {
500
+ flex: 1,
501
+ alignItems: "center",
502
+ justifyContent: "center",
503
+ padding: 32,
504
+ gap: 12
505
+ },
506
+ emptyTitle: {
507
+ fontSize: 16,
508
+ fontWeight: "600",
509
+ color: buoyColors.text
510
+ },
511
+ emptyText: {
512
+ fontSize: 13,
513
+ color: buoyColors.textSecondary,
514
+ textAlign: "center",
515
+ lineHeight: 18
516
+ },
517
+ viewToggleContainer: {
518
+ flexDirection: "row",
519
+ padding: 12,
520
+ gap: 8
521
+ },
522
+ viewToggleCard: {
523
+ flex: 1,
524
+ backgroundColor: buoyColors.card,
525
+ borderRadius: 8,
526
+ borderWidth: 1,
527
+ borderColor: buoyColors.border,
528
+ padding: 10,
529
+ gap: 4
530
+ },
531
+ viewToggleCardActive: {
532
+ borderColor: buoyColors.primary,
533
+ backgroundColor: buoyColors.primary + "10"
534
+ },
535
+ viewToggleCardDisabled: {
536
+ opacity: 0.5
537
+ },
538
+ todoBadge: {
539
+ backgroundColor: buoyColors.textMuted + "30",
540
+ paddingHorizontal: 6,
541
+ paddingVertical: 2,
542
+ borderRadius: 4,
543
+ marginLeft: 4
544
+ },
545
+ todoBadgeText: {
546
+ fontSize: 9,
547
+ fontWeight: "700",
548
+ color: buoyColors.textMuted,
549
+ letterSpacing: 0.3
550
+ },
551
+ viewToggleContent: {
552
+ flexDirection: "row",
553
+ alignItems: "center",
554
+ gap: 6
555
+ },
556
+ viewToggleLabel: {
557
+ fontSize: 10,
558
+ fontWeight: "700",
559
+ color: buoyColors.textSecondary,
560
+ letterSpacing: 0.5
561
+ },
562
+ viewToggleLabelActive: {
563
+ color: buoyColors.primary
564
+ },
565
+ viewToggleLabelDisabled: {
566
+ color: buoyColors.textMuted
567
+ },
568
+ viewToggleDescription: {
569
+ fontSize: 11,
570
+ color: buoyColors.textMuted,
571
+ marginTop: 2
572
+ },
573
+ contentScroll: {
574
+ flex: 1
575
+ },
576
+ contentContainer: {
577
+ padding: 12,
578
+ paddingTop: 0
579
+ },
580
+ noEventContainer: {
581
+ padding: 24,
582
+ alignItems: "center"
583
+ },
584
+ noEventText: {
585
+ fontSize: 13,
586
+ color: buoyColors.textMuted
587
+ },
588
+ currentStateContainer: {
589
+ gap: 12
590
+ },
591
+ eventHeader: {
592
+ flexDirection: "row",
593
+ alignItems: "center",
594
+ justifyContent: "space-between",
595
+ backgroundColor: buoyColors.card,
596
+ borderRadius: 8,
597
+ borderWidth: 1,
598
+ borderColor: buoyColors.border,
599
+ padding: 12
600
+ },
601
+ eventHeaderLeft: {
602
+ gap: 2
603
+ },
604
+ eventTitle: {
605
+ fontSize: 15,
606
+ fontWeight: "700",
607
+ color: buoyColors.text
608
+ },
609
+ eventTime: {
610
+ fontSize: 11,
611
+ color: buoyColors.textMuted,
612
+ fontFamily: "monospace"
613
+ },
614
+ eventBadge: {
615
+ flexDirection: "row",
616
+ alignItems: "center",
617
+ paddingHorizontal: 10,
618
+ paddingVertical: 4,
619
+ borderRadius: 12,
620
+ gap: 6
621
+ },
622
+ eventBadgeDot: {
623
+ width: 6,
624
+ height: 6,
625
+ borderRadius: 3
626
+ },
627
+ eventBadgeText: {
628
+ fontSize: 10,
629
+ fontWeight: "700",
630
+ fontFamily: "monospace"
631
+ },
632
+ section: {
633
+ backgroundColor: buoyColors.card,
634
+ borderRadius: 8,
635
+ borderWidth: 1,
636
+ borderColor: buoyColors.border,
637
+ overflow: "hidden"
638
+ },
639
+ sectionContent: {
640
+ padding: 12,
641
+ paddingTop: 8
642
+ },
643
+ causeContainer: {
644
+ marginBottom: 12
645
+ },
646
+ changedList: {
647
+ marginBottom: 12,
648
+ paddingLeft: 4
649
+ },
650
+ changedListTitle: {
651
+ fontSize: 11,
652
+ fontWeight: "600",
653
+ color: buoyColors.textSecondary,
654
+ marginBottom: 6
655
+ },
656
+ changedItem: {
657
+ fontSize: 12,
658
+ color: buoyColors.text,
659
+ fontFamily: "monospace",
660
+ marginBottom: 3,
661
+ paddingLeft: 8
662
+ },
663
+ propsScrollView: {
664
+ maxHeight: 150
665
+ },
666
+ propsJson: {
667
+ fontSize: 11,
668
+ fontFamily: "monospace",
669
+ color: buoyColors.text,
670
+ backgroundColor: buoyColors.input,
671
+ padding: 10,
672
+ borderRadius: 6
673
+ },
674
+ diffContainer: {
675
+ gap: 12
676
+ },
677
+ compareBar: {
678
+ flexDirection: "row",
679
+ backgroundColor: buoyColors.card,
680
+ borderRadius: 8,
681
+ borderWidth: 1,
682
+ borderColor: buoyColors.border,
683
+ padding: 10
684
+ },
685
+ compareSide: {
686
+ flex: 1,
687
+ gap: 4
688
+ },
689
+ compareLabelRow: {
690
+ flexDirection: "row",
691
+ alignItems: "center",
692
+ gap: 8
693
+ },
694
+ compareLabel: {
695
+ fontSize: 10,
696
+ fontWeight: "700",
697
+ letterSpacing: 0.5
698
+ },
699
+ compareActionBadge: {
700
+ paddingHorizontal: 6,
701
+ paddingVertical: 2,
702
+ borderRadius: 4
703
+ },
704
+ compareActionText: {
705
+ fontSize: 9,
706
+ fontWeight: "600"
707
+ },
708
+ compareMeta: {
709
+ flexDirection: "row",
710
+ alignItems: "center",
711
+ gap: 6
712
+ },
713
+ compareIndex: {
714
+ fontSize: 12,
715
+ fontWeight: "600",
716
+ color: buoyColors.text
717
+ },
718
+ compareTime: {
719
+ fontSize: 10,
720
+ color: buoyColors.textMuted,
721
+ fontFamily: "monospace"
722
+ },
723
+ compareDivider: {
724
+ width: 30,
725
+ alignItems: "center",
726
+ justifyContent: "center"
727
+ },
728
+ compareArrow: {
729
+ fontSize: 16,
730
+ color: buoyColors.textMuted
731
+ },
732
+ diffSummary: {
733
+ backgroundColor: buoyColors.card,
734
+ borderRadius: 8,
735
+ borderWidth: 1,
736
+ borderColor: buoyColors.border,
737
+ padding: 12
738
+ },
739
+ diffSummaryTitle: {
740
+ fontSize: 11,
741
+ fontWeight: "600",
742
+ color: buoyColors.textSecondary,
743
+ marginBottom: 8
744
+ },
745
+ diffChanges: {
746
+ gap: 4
747
+ },
748
+ diffChangeItem: {
749
+ flexDirection: "row",
750
+ alignItems: "center",
751
+ gap: 6
752
+ },
753
+ diffChangeIcon: {
754
+ fontSize: 12,
755
+ fontWeight: "700",
756
+ color: buoyColors.warning,
757
+ fontFamily: "monospace"
758
+ },
759
+ diffChangeKey: {
760
+ fontSize: 12,
761
+ color: buoyColors.text,
762
+ fontFamily: "monospace"
763
+ },
764
+ diffNoChanges: {
765
+ fontSize: 12,
766
+ color: buoyColors.textMuted,
767
+ fontStyle: "italic"
768
+ },
769
+ diffPlaceholder: {
770
+ fontSize: 12,
771
+ color: buoyColors.textMuted,
772
+ fontStyle: "italic",
773
+ textAlign: "center",
774
+ padding: 16
775
+ },
776
+ // Diff mode tabs
777
+ diffModeTabs: {
778
+ flexDirection: "row",
779
+ backgroundColor: buoyColors.card,
780
+ borderRadius: 8,
781
+ borderWidth: 1,
782
+ borderColor: buoyColors.border,
783
+ padding: 4,
784
+ gap: 4
785
+ },
786
+ diffModeTab: {
787
+ flex: 1,
788
+ paddingVertical: 8,
789
+ paddingHorizontal: 12,
790
+ borderRadius: 6,
791
+ alignItems: "center"
792
+ },
793
+ diffModeTabActive: {
794
+ backgroundColor: buoyColors.primary + "20"
795
+ },
796
+ diffModeTabDisabled: {
797
+ opacity: 0.4
798
+ },
799
+ diffModeTabText: {
800
+ fontSize: 10,
801
+ fontWeight: "600",
802
+ color: buoyColors.textSecondary,
803
+ letterSpacing: 0.5
804
+ },
805
+ diffModeTabTextActive: {
806
+ color: buoyColors.primary
807
+ },
808
+ diffModeTabTextDisabled: {
809
+ color: buoyColors.textMuted
810
+ },
811
+ // Cause diff styles
812
+ causeDiffContainer: {
813
+ gap: 8,
814
+ marginBottom: 12
815
+ },
816
+ causeDiffRow: {
817
+ flexDirection: "row",
818
+ alignItems: "center",
819
+ gap: 8
820
+ },
821
+ causeDiffLabel: {
822
+ fontSize: 11,
823
+ fontWeight: "600",
824
+ color: buoyColors.textSecondary,
825
+ minWidth: 80
826
+ },
827
+ causeDiffValue: {
828
+ fontSize: 12,
829
+ color: buoyColors.text,
830
+ fontFamily: "monospace",
831
+ fontWeight: "500"
832
+ },
833
+ diffChangesTitle: {
834
+ fontSize: 11,
835
+ fontWeight: "600",
836
+ color: buoyColors.textSecondary,
837
+ marginBottom: 6
838
+ },
839
+ // Tree diff container
840
+ treeDiffContainer: {
841
+ backgroundColor: buoyColors.card,
842
+ borderRadius: 8,
843
+ borderWidth: 1,
844
+ borderColor: buoyColors.border,
845
+ overflow: "hidden",
846
+ minHeight: 150
847
+ },
848
+ noDiffData: {
849
+ padding: 24,
850
+ alignItems: "center",
851
+ gap: 8
852
+ },
853
+ noDiffDataTitle: {
854
+ fontSize: 13,
855
+ fontWeight: "600",
856
+ color: buoyColors.text
857
+ },
858
+ noDiffDataText: {
859
+ fontSize: 12,
860
+ color: buoyColors.textMuted,
861
+ textAlign: "center",
862
+ lineHeight: 18
863
+ },
864
+ lockedBanner: {
865
+ flexDirection: "row",
866
+ alignItems: "center",
867
+ justifyContent: "center",
868
+ paddingVertical: 10,
869
+ paddingHorizontal: 16,
870
+ marginHorizontal: 12,
871
+ marginTop: 8,
872
+ backgroundColor: buoyColors.warning + "15",
873
+ borderRadius: 8,
874
+ borderWidth: 1,
875
+ borderColor: buoyColors.warning + "30",
876
+ gap: 8
877
+ },
878
+ lockedBannerText: {
879
+ color: buoyColors.warning,
880
+ fontSize: 13,
881
+ fontWeight: "500"
882
+ },
883
+ lockedBannerSubtext: {
884
+ color: buoyColors.textMuted,
885
+ fontSize: 12
886
+ }
887
+ });
888
+ export default RenderHistoryViewer;