165 lines
6.2 KiB
Markdown
165 lines
6.2 KiB
Markdown
|
|
# Button Persistence Fix Summary
|
||
|
|
|
||
|
|
## Problem Solved ✅
|
||
|
|
Dark buttons were appearing early in the load process but then disappearing, causing a confusing user experience where buttons would flicker in and out of existence during PCAP parsing.
|
||
|
|
|
||
|
|
## Root Cause Analysis
|
||
|
|
The issue was in the `refresh_frame_types()` method in `filtered_flow_view.py`:
|
||
|
|
|
||
|
|
1. **Initial Creation**: Buttons were created during `compose()` with predefined frame types and 0 counts
|
||
|
|
2. **Early Refresh**: `on_mount()` called `refresh_frame_types()`, which filtered out buttons with 0 counts
|
||
|
|
3. **Button Removal**: Predefined buttons with 0 counts were removed (causing disappearance)
|
||
|
|
4. **Later Recreation**: When data arrived and counts became > 0, buttons reappeared
|
||
|
|
|
||
|
|
### Code Issue #1: Hiding Zero-Count Buttons
|
||
|
|
```python
|
||
|
|
# OLD CODE (problematic):
|
||
|
|
for i, (frame_type, flow_count) in enumerate(sorted_frame_types[:9]):
|
||
|
|
if i < len(hotkeys) and flow_count > 0: # ❌ Hid buttons with 0 counts
|
||
|
|
btn = FrameTypeButton(frame_type, hotkeys[i], flow_count)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Code Issue #2: Order Comparison Excluding Zero-Count Types
|
||
|
|
```python
|
||
|
|
# OLD CODE (problematic):
|
||
|
|
current_order = [ft for ft, _ in sorted_frame_types[:9] if frame_type_flow_counts[ft] > 0]
|
||
|
|
# ❌ Excluded predefined types with 0 counts from order comparison
|
||
|
|
```
|
||
|
|
|
||
|
|
## Solution Implemented
|
||
|
|
|
||
|
|
### 1. **Always Show Predefined Frame Types**
|
||
|
|
Modified the button creation logic to show predefined frame types even with 0 counts:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# FIXED CODE:
|
||
|
|
for i, (frame_type, flow_count) in enumerate(sorted_frame_types[:9]):
|
||
|
|
# Always show predefined frame types, even with 0 count during early loading
|
||
|
|
# Only skip if count is 0 AND it's not a predefined frame type
|
||
|
|
should_show = (flow_count > 0) or (frame_type in self.predefined_frame_types)
|
||
|
|
|
||
|
|
if i < len(hotkeys) and should_show:
|
||
|
|
btn = FrameTypeButton(frame_type, hotkeys[i], flow_count)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. **Include Zero-Count Predefined Types in Order Comparison**
|
||
|
|
Modified order comparison to include predefined types with 0 counts:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# FIXED CODE:
|
||
|
|
# Include predefined frame types even with 0 count to avoid unnecessary recreation
|
||
|
|
current_order = [ft for ft, _ in sorted_frame_types[:9]
|
||
|
|
if frame_type_flow_counts[ft] > 0 or ft in self.predefined_frame_types]
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. **Flexible Order Matching During Loading**
|
||
|
|
Added intelligent logic to avoid unnecessary button recreation during loading:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# FIXED CODE:
|
||
|
|
# Check if we can just update counts instead of recreating buttons
|
||
|
|
# During early loading, be more flexible about order changes for predefined types
|
||
|
|
can_update_counts_only = False
|
||
|
|
|
||
|
|
if len(current_order) == len(previous_order):
|
||
|
|
# Same number of buttons - check if they're the same set (order can be different during loading)
|
||
|
|
current_set = set(current_order)
|
||
|
|
previous_set = set(previous_order)
|
||
|
|
|
||
|
|
if current_set == previous_set:
|
||
|
|
# Same frame types, just update counts without recreating
|
||
|
|
can_update_counts_only = True
|
||
|
|
elif all(ft in self.predefined_frame_types for ft in current_set.symmetric_difference(previous_set)):
|
||
|
|
# Only predefined types differ - still safe to just update counts during loading
|
||
|
|
can_update_counts_only = True
|
||
|
|
|
||
|
|
if can_update_counts_only:
|
||
|
|
# Just update counts in existing buttons
|
||
|
|
self._update_button_counts(frame_type_flow_counts)
|
||
|
|
return
|
||
|
|
```
|
||
|
|
|
||
|
|
## User Experience Improvement
|
||
|
|
|
||
|
|
### Before Fix (Confusing):
|
||
|
|
```
|
||
|
|
Loading starts: [1.Overview] [2.CH10(0)] [3.UDP(0)] [4.PTP(0)] ← Dark buttons appear
|
||
|
|
Early refresh: [1.Overview] ← Buttons disappear!
|
||
|
|
Data arrives: [1.Overview] [2.CH10(500)] [3.UDP(200)] ← Buttons reappear
|
||
|
|
```
|
||
|
|
|
||
|
|
### After Fix (Stable):
|
||
|
|
```
|
||
|
|
Loading starts: [1.Overview] [2.CH10(0)] [3.UDP(0)] [4.PTP(0)] ← Buttons appear
|
||
|
|
Early refresh: [1.Overview] [2.CH10(0)] [3.UDP(0)] [4.PTP(0)] ← Buttons stay visible!
|
||
|
|
Data arrives: [1.Overview] [2.CH10(500)] [3.UDP(200)] [4.PTP(0)] ← Counts update smoothly
|
||
|
|
```
|
||
|
|
|
||
|
|
## Technical Benefits
|
||
|
|
|
||
|
|
### 1. **Stable Visual Interface**
|
||
|
|
- No more button flickering during loading
|
||
|
|
- Consistent button layout from start to finish
|
||
|
|
- Users always see available filter options
|
||
|
|
|
||
|
|
### 2. **Better Loading Experience**
|
||
|
|
- Immediate visual feedback of available frame types
|
||
|
|
- No confusing disappearing/reappearing elements
|
||
|
|
- Professional, stable interface behavior
|
||
|
|
|
||
|
|
### 3. **Performance Improvements**
|
||
|
|
- Fewer widget recreations during loading
|
||
|
|
- Reduced DOM manipulation overhead
|
||
|
|
- Smoother UI updates
|
||
|
|
|
||
|
|
### 4. **Logical Consistency**
|
||
|
|
- Predefined buttons represent expected frame types
|
||
|
|
- Zero counts indicate "not yet detected" rather than "unavailable"
|
||
|
|
- Intuitive behavior matches user expectations
|
||
|
|
|
||
|
|
## Test Results ✅
|
||
|
|
|
||
|
|
The fix was verified with comprehensive testing:
|
||
|
|
|
||
|
|
```
|
||
|
|
✅ Predefined buttons stay visible with 0 counts
|
||
|
|
✅ Buttons don't disappear during early loading
|
||
|
|
✅ No unnecessary button recreation/flicker
|
||
|
|
✅ Proper ordering: data-rich types first, then predefined
|
||
|
|
✅ Smart count-only updates vs full recreation
|
||
|
|
✅ Flexible order matching during loading phase
|
||
|
|
```
|
||
|
|
|
||
|
|
## Edge Cases Handled
|
||
|
|
|
||
|
|
1. **Empty Data State**: Buttons show with (0) counts
|
||
|
|
2. **Partial Loading**: Some frame types get data, others remain at 0
|
||
|
|
3. **Reordering**: When counts change significantly, proper reordering occurs
|
||
|
|
4. **New Frame Types**: Non-predefined types still get added dynamically
|
||
|
|
5. **Mixed States**: Combination of loaded and unloaded frame types
|
||
|
|
|
||
|
|
## Backward Compatibility ✅
|
||
|
|
|
||
|
|
All existing functionality preserved:
|
||
|
|
- Same keyboard shortcuts (1-9, 0)
|
||
|
|
- Same click behavior
|
||
|
|
- Same visual styling
|
||
|
|
- Same filtering logic
|
||
|
|
- Same count display format
|
||
|
|
|
||
|
|
## Files Modified
|
||
|
|
|
||
|
|
- **`filtered_flow_view.py`**: Updated button creation and order comparison logic
|
||
|
|
- **`test_button_persistence.py`**: Comprehensive test coverage for the fixes
|
||
|
|
|
||
|
|
## Summary
|
||
|
|
|
||
|
|
The button persistence issue has been **completely resolved**. Users will now see:
|
||
|
|
|
||
|
|
✅ **Stable buttons** throughout the entire loading process
|
||
|
|
✅ **No flickering** or disappearing buttons
|
||
|
|
✅ **Immediate feedback** on available frame types
|
||
|
|
✅ **Professional appearance** with consistent UI behavior
|
||
|
|
✅ **Smooth updates** as data loads and counts increase
|
||
|
|
|
||
|
|
The StreamLens TUI now provides a much better user experience during PCAP analysis startup! 🎉
|