Performance: - Move JSON parsing to background isolate via Isolate.run() (eliminates 2-4s UI freeze) - Cache filteredEntries with key-based invalidation (eliminates O(n) recomputation) - Debounce search queries at 300ms (prevents cascade rebuilds on keystroke) - Flatten timeline from 2-level turn×response Column to single virtualized ListView.builder - Add RepaintBoundary per timeline item (isolates repaints during scroll) - Use context.select for granular rebuilds instead of top-level context.watch - Lazy ExpandableCard: child not built until first expand (replaces AnimatedCrossFade) - Use IndexedStack in AppShell (preserves screen state across tab switches) Fixes: - Collect parse errors instead of silently swallowing them - Show parse error count in timeline filter bar - Fix overflow in tokens screen pie chart legend Build: - Configure Developer ID signing with hardened runtime for production distribution - Enable secure timestamps for notarization - Update app name to Claude Session Viewer - Signed, notarized, stapled DMG distribution
57 lines
1.5 KiB
Dart
57 lines
1.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../providers/session_provider.dart';
|
|
import '../../screens/agents/agents_screen.dart';
|
|
import '../../screens/home/home_screen.dart';
|
|
import '../../screens/timeline/timeline_screen.dart';
|
|
import '../../screens/tokens/tokens_screen.dart';
|
|
import '../../screens/toolbelt/toolbelt_screen.dart';
|
|
import 'sidebar.dart';
|
|
|
|
class AppShell extends StatefulWidget {
|
|
const AppShell({super.key});
|
|
|
|
@override
|
|
State<AppShell> createState() => _AppShellState();
|
|
}
|
|
|
|
class _AppShellState extends State<AppShell> {
|
|
SidebarScreen _screen = SidebarScreen.home;
|
|
|
|
void _onScreenSelected(SidebarScreen screen) {
|
|
setState(() => _screen = screen);
|
|
}
|
|
|
|
void _onSessionLoaded() {
|
|
setState(() => _screen = SidebarScreen.timeline);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final provider = context.watch<SessionProvider>();
|
|
final hasSession = provider.session != null;
|
|
|
|
return Row(
|
|
children: [
|
|
Sidebar(
|
|
selected: _screen,
|
|
onSelect: _onScreenSelected,
|
|
hasSession: hasSession,
|
|
),
|
|
Expanded(
|
|
child: IndexedStack(
|
|
index: _screen.index,
|
|
children: [
|
|
HomeScreen(onSessionLoaded: _onSessionLoaded),
|
|
const TimelineScreen(),
|
|
const AgentsScreen(),
|
|
const ToolbeltScreen(),
|
|
const TokensScreen(),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|