Skip to content

Commit 4a44185

Browse files
authored
Merge pull request #2 from samsel/claude/rust-cli-exploration-FOYmP
Add performance profiler with real-time overlay UI
2 parents e204ce1 + d668703 commit 4a44185

6 files changed

Lines changed: 739 additions & 24 deletions

File tree

src/App.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useEffect, useCallback, useRef, useMemo, type MutableRefObject } from 'react'
22
import InputScreen from '@/components/InputScreen'
33
import ParsingProgress from '@/components/ParsingProgress'
4+
import ProfilerOverlay from '@/components/ProfilerOverlay'
45
import { calculateStats } from '@/lib/stats'
56
import { calculateFunStats } from '@/lib/funStats'
67
import { generateInsights } from '@/lib/insights'
@@ -12,6 +13,7 @@ import RevealSequence from '@/components/dashboard/RevealSequence'
1213
import ErrorBoundary from '@/components/ErrorBoundary'
1314
import { DEMO_FLIGHTS } from '@/components/landing/demoFlights'
1415
import type { Flight, ParseProgress, WorkerOutMessage } from '@/lib/types'
16+
import type { ProfilerReport } from '@/lib/profiler'
1517

1618
type AppState = 'landing' | 'parsing' | 'reveal' | 'results'
1719

@@ -27,8 +29,11 @@ function App() {
2729
const [error, setError] = useState<string | null>(null)
2830
const [cachedData, setCachedData] = useState<SyncData | null>(null)
2931
const [lastImportAt, setLastImportAt] = useState<string | null>(null)
32+
const [profilerEnabled, setProfilerEnabled] = useState(false)
33+
const [profilerReport, setProfilerReport] = useState<ProfilerReport | null>(null)
3034
const workerRef = useRef<Worker | null>(null)
3135
const generationRef = useRef(0) as MutableRefObject<number>
36+
const profilerEnabledRef = useRef(false)
3237
// Existing flights for merging with new imports
3338
const existingFlightsRef = useRef<Flight[]>([])
3439

@@ -63,6 +68,9 @@ function App() {
6368
setAppState(merged.length > 0 ? 'reveal' : 'results')
6469
break
6570
}
71+
case 'profiler-report':
72+
setProfilerReport(msg.data)
73+
break
6674
case 'error':
6775
setError(msg.data.message)
6876
setProgress((p) => ({ ...p, phase: 'error', message: msg.data.message }))
@@ -77,6 +85,7 @@ function App() {
7785
}
7886

7987
workerRef.current = worker
88+
worker.postMessage({ type: 'set-profiler', data: profilerEnabledRef.current })
8089
worker.postMessage({ type: 'init-llm' })
8190
return worker
8291
}, [])
@@ -94,9 +103,18 @@ function App() {
94103
// eslint-disable-next-line react-hooks/exhaustive-deps
95104
}, [])
96105

106+
const handleProfilerToggle = useCallback(() => {
107+
const next = !profilerEnabledRef.current
108+
profilerEnabledRef.current = next
109+
setProfilerEnabled(next)
110+
if (!next) setProfilerReport(null)
111+
workerRef.current?.postMessage({ type: 'set-profiler', data: next })
112+
}, [])
113+
97114
const handleFileUpload = useCallback((files: File[]) => {
98115
setAppState('parsing')
99116
setError(null)
117+
setProfilerReport(null)
100118
setProgress({ phase: 'scanning', current: 0, total: 0, flightsFound: 0, message: 'Preparing files...' })
101119

102120
// Preserve existing flights for merge
@@ -108,6 +126,8 @@ function App() {
108126
if (!granted) console.warn('Durable storage not granted. Model cache may be evicted')
109127
})
110128

129+
// Ensure worker has latest profiler state
130+
workerRef.current?.postMessage({ type: 'set-profiler', data: profilerEnabledRef.current })
111131
// Send File objects directly to worker (structured-cloneable)
112132
// No FileReader needed -- the worker streams them with File.stream()
113133
workerRef.current?.postMessage({ type: 'parse-mbox-files', data: files })
@@ -149,6 +169,14 @@ function App() {
149169
existingFlightsRef.current = []
150170
}, [createWorker])
151171

172+
const profilerOverlay = (
173+
<ProfilerOverlay
174+
enabled={profilerEnabled}
175+
onToggle={handleProfilerToggle}
176+
report={profilerReport}
177+
/>
178+
)
179+
152180
if (appState === 'landing') {
153181
return (
154182
<div className="animate-fade-in">
@@ -167,6 +195,7 @@ function App() {
167195
</button>
168196
</div>
169197
)}
198+
{profilerOverlay}
170199
</div>
171200
)
172201
}
@@ -176,18 +205,22 @@ function App() {
176205
<div className="min-h-screen glass-bg text-white flex flex-col items-center justify-center px-4 animate-fade-in">
177206
<h1 className="text-3xl font-bold mb-8">FlightWrapped</h1>
178207
<ParsingProgress progress={progress} onReset={resetToLanding} />
208+
{profilerOverlay}
179209
</div>
180210
)
181211
}
182212

183213
if (appState === 'reveal') {
184214
return (
185-
<RevealSequence
186-
stats={stats}
187-
funStats={funStats}
188-
archetype={archetype}
189-
onComplete={() => setAppState('results')}
190-
/>
215+
<>
216+
<RevealSequence
217+
stats={stats}
218+
funStats={funStats}
219+
archetype={archetype}
220+
onComplete={() => setAppState('results')}
221+
/>
222+
{profilerOverlay}
223+
</>
191224
)
192225
}
193226

@@ -205,6 +238,7 @@ function App() {
205238
onFileUpload={handleFileUpload}
206239
/>
207240
</div>
241+
{profilerOverlay}
208242
</ErrorBoundary>
209243
)
210244
}

0 commit comments

Comments
 (0)