perf: Phase 1 critical performance fixes + production macOS build

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
This commit is contained in:
Mathias Beaulieu-Duncan
2026-04-07 13:32:13 -04:00
parent aa484f6409
commit 659dade82d
8 changed files with 862 additions and 410 deletions
+15 -12
View File
@@ -26,11 +26,13 @@ class _ExpandableCardState extends State<ExpandableCard>
late bool _expanded;
late AnimationController _controller;
late Animation<double> _rotation;
bool _hasBeenExpanded = false; // Track if ever expanded to lazy-build
@override
void initState() {
super.initState();
_expanded = widget.initiallyExpanded;
_hasBeenExpanded = _expanded;
_controller = AnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
@@ -51,6 +53,7 @@ class _ExpandableCardState extends State<ExpandableCard>
setState(() {
_expanded = !_expanded;
if (_expanded) {
_hasBeenExpanded = true;
_controller.forward();
} else {
_controller.reverse();
@@ -76,7 +79,8 @@ class _ExpandableCardState extends State<ExpandableCard>
onTap: _toggle,
borderRadius: BorderRadius.circular(8),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
child: Row(
children: [
RotationTransition(
@@ -93,19 +97,18 @@ class _ExpandableCardState extends State<ExpandableCard>
),
),
),
ClipRect(
child: AnimatedCrossFade(
firstChild: const SizedBox.shrink(),
secondChild: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
child: widget.child,
// Lazy animated expand: only build child once ever expanded
if (_hasBeenExpanded)
ClipRect(
child: SizeTransition(
sizeFactor: _controller,
axisAlignment: -1.0,
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
child: widget.child,
),
),
crossFadeState: _expanded
? CrossFadeState.showSecond
: CrossFadeState.showFirst,
duration: const Duration(milliseconds: 200),
),
),
],
),
);