Date: 2026-01-17
The continuous repaint issue was caused by hover sensing on the scroll area content in the rendered markdown editor.
In src/markdown/editor.rs line 796 (now ~798), the rendered editor's scroll area content returned:
ui.allocate_response(Vec2::ZERO, egui::Sense::hover())This Sense::hover() caused egui to:
- Continuously track hover state across the entire rendered content area
- Request immediate repaints whenever the mouse moved over the area
- Bypass the 100ms throttling in
needs_continuous_repaint()
The app's throttling works like this:
if !self.needs_continuous_repaint() {
ctx.request_repaint_after(std::time::Duration::from_millis(100));
}However, when egui's internal hover tracking calls ctx.request_repaint() (immediate), it takes priority over request_repaint_after(100ms). Result: ~60fps instead of ~10fps.
Changed the scroll area content response from Sense::hover() to Sense::focusable_noninteractive():
// Before (caused continuous repaints):
ui.allocate_response(Vec2::ZERO, egui::Sense::hover())
// After (fixes the issue):
ui.allocate_response(Vec2::ZERO, egui::Sense::focusable_noninteractive())This removes the hover tracking overhead while maintaining scroll area functionality.
Added frame rate tracking to help verify the fix works:
// In FerriteApp struct (debug builds only)
#[cfg(debug_assertions)]
frame_count: u64,
#[cfg(debug_assertions)]
last_fps_log: std::time::Instant,
// In update() - logs FPS every 5 seconds
[REPAINT_DEBUG] FPS: X.X, needs_continuous_repaint: false, frames: Nsrc/markdown/editor.rs- ChangedSense::hover()toSense::focusable_noninteractive()on scroll area contentsrc/app.rs- Added FPS diagnostic logging (debug builds only)
To verify the fix works:
- Build in debug mode:
cargo build - Run the app:
cargo run - Open a markdown file with lists
- Switch to Rendered mode
- Leave the app idle for 30 seconds with mouse over the rendered content
- Check the logs for
[REPAINT_DEBUG]messages - Expected: FPS should be ~10 (100ms intervals) not ~60
- Idle CPU usage: Should drop to <5% on Intel Macs
- Frame rate when idle: ~10fps (100ms intervals) instead of ~60fps
- Rendered mode: Only re-renders when actual interaction occurs
- Analysis document:
docs/technical/platform/intel-mac-cpu-issue-analysis.md - Original log file:
docs/ferrite_macos_intel_log.txt(48,926 lines) - Log analysis script:
scripts/analyze_log.py
Click to expand original handover prompt
-
Symptom: In Rendered (WYSIWYG) mode, the app renders at ~60fps continuously instead of throttling to 100ms intervals when idle
-
Evidence from logs:
- 48,926 log lines in 22 seconds = ~2,224 lines/second
- ~37 frames per second (each frame generates ~60 log lines for list items)
- The app SHOULD be using
ctx.request_repaint_after(100ms)when idle (10fps max)
-
The throttling logic existed in
src/app.rsaround line 7077:if !self.needs_continuous_repaint() { ctx.request_repaint_after(std::time::Duration::from_millis(100)); }
-
needs_continuous_repaint()was working correctly - It checks for pipeline running, toast messages, dialogs, etc. -
egui was requesting repaints internally due to:
Sense::hover()on scroll area content (PRIMARY CAUSE)- Multiple hover-sensing elements in the rendered editor
-
8 instances of hover sensing in
editor.rs:Sense::hover()on scroll area content- Multiple
.hovered()checks for cursor icon changes on_hover_text()calls (tooltips)