feat: implement profiling run import and remove overhead toggle
This commit is contained in:
43
src/App.tsx
43
src/App.tsx
@@ -6,7 +6,7 @@ import {
|
|||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
import {
|
import {
|
||||||
Activity, Cpu, Server, Database, Play, Square,
|
Activity, Cpu, Server, Database, Play, Square,
|
||||||
AlertTriangle, ArrowLeft, Shield, CheckSquare, Square as SquareIcon, Save, X
|
AlertTriangle, ArrowLeft, Shield, Save, X, Download
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { clsx } from 'clsx';
|
import { clsx } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
@@ -70,7 +70,6 @@ function App() {
|
|||||||
const [view, setView] = useState<'dashboard' | 'report'>('dashboard');
|
const [view, setView] = useState<'dashboard' | 'report'>('dashboard');
|
||||||
const [stats, setStats] = useState<SystemStats | null>(null);
|
const [stats, setStats] = useState<SystemStats | null>(null);
|
||||||
const [history, setHistory] = useState<{ time: string; cpu: number }[]>([]);
|
const [history, setHistory] = useState<{ time: string; cpu: number }[]>([]);
|
||||||
const [excludeSelf, setExcludeSelf] = useState(true);
|
|
||||||
const [report, setReport] = useState<ProfilingReport | null>(null);
|
const [report, setReport] = useState<ProfilingReport | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -78,7 +77,7 @@ function App() {
|
|||||||
try {
|
try {
|
||||||
const isRecording = stats?.is_recording ?? false;
|
const isRecording = stats?.is_recording ?? false;
|
||||||
const data = await invoke<SystemStats>('get_system_stats', {
|
const data = await invoke<SystemStats>('get_system_stats', {
|
||||||
excludeSelf,
|
excludeSelf: true,
|
||||||
minimal: isRecording || view === 'report'
|
minimal: isRecording || view === 'report'
|
||||||
});
|
});
|
||||||
setStats(data);
|
setStats(data);
|
||||||
@@ -98,7 +97,7 @@ function App() {
|
|||||||
fetchStats();
|
fetchStats();
|
||||||
const interval = setInterval(fetchStats, 1000);
|
const interval = setInterval(fetchStats, 1000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [excludeSelf, view, stats?.is_recording]);
|
}, [view, stats?.is_recording]);
|
||||||
|
|
||||||
const toggleRecording = async () => {
|
const toggleRecording = async () => {
|
||||||
if (stats?.is_recording) {
|
if (stats?.is_recording) {
|
||||||
@@ -110,6 +109,27 @@ function App() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleImport = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.target?.result as string) as ProfilingReport;
|
||||||
|
if (data.aggregated_processes && data.timeline) {
|
||||||
|
setReport(data);
|
||||||
|
setView('report');
|
||||||
|
} else {
|
||||||
|
alert('Invalid report format');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert('Failed to parse JSON');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
};
|
||||||
|
|
||||||
const killProcess = async (pid: number) => {
|
const killProcess = async (pid: number) => {
|
||||||
try {
|
try {
|
||||||
await invoke('run_as_admin', { command: `kill -9 ${pid}` });
|
await invoke('run_as_admin', { command: `kill -9 ${pid}` });
|
||||||
@@ -127,7 +147,7 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stats) return <div className="h-screen w-screen flex items-center justify-center text-text bg-base">Loading System Data...</div>;
|
if (!stats) return <div className="h-screen w-screen flex items-center justify-center text-text bg-base font-black italic tracking-tighter uppercase text-2xl">Initializing SysPulse...</div>;
|
||||||
|
|
||||||
const avgCpu = stats.cpu_usage.reduce((a, b) => a + b, 0) / stats.cpu_usage.length;
|
const avgCpu = stats.cpu_usage.reduce((a, b) => a + b, 0) / stats.cpu_usage.length;
|
||||||
const memoryPercent = (stats.used_memory / stats.total_memory) * 100;
|
const memoryPercent = (stats.used_memory / stats.total_memory) * 100;
|
||||||
@@ -142,13 +162,12 @@ function App() {
|
|||||||
<span className="font-black tracking-tighter uppercase italic text-xl">SysPulse</span>
|
<span className="font-black tracking-tighter uppercase italic text-xl">SysPulse</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4 px-4">
|
<div className="flex items-center gap-4 px-4">
|
||||||
<button
|
<label className="flex items-center gap-2 text-subtext0 hover:text-text transition-colors text-[10px] font-black uppercase tracking-widest cursor-pointer group">
|
||||||
className="flex items-center gap-2 text-subtext0 hover:text-text transition-colors text-xs font-bold"
|
<Download size={14} className="text-blue group-hover:scale-110 transition-transform" />
|
||||||
onClick={() => setExcludeSelf(!excludeSelf)}
|
<span>Import Run</span>
|
||||||
>
|
<input type="file" accept=".json" onChange={handleImport} className="hidden" />
|
||||||
{excludeSelf ? <CheckSquare size={14} className="text-blue" /> : <SquareIcon size={14} />}
|
</label>
|
||||||
<span>HIDE OVERHEAD</span>
|
<div className="h-4 w-px bg-surface1 mx-1" />
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-2 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-xl",
|
"flex items-center gap-2 px-6 py-2 rounded-xl text-xs font-black transition-all shadow-xl",
|
||||||
|
|||||||
Reference in New Issue
Block a user