Conversation
|
For completeness, here is the accompanying LLM output for the first commit of this PR. 1. 🔴 Draw happens before event handling (1-event visual lag)This is the biggest architectural issue. Look at the main loop: /// kdash/src/main.rs#L271-L278
// draw the UI layout
terminal.draw(|f| ui::draw(f, &mut app))?;
// handle events
match event {
event::Event::Input(key_event) => {
info!("Input event received: {:?}", key_event);
// quit on CTRL + CThe sequence on every keypress is:
So every scroll action is visually delayed by one event cycle. If no other events arrive quickly, that's up to 250ms (one tick) before you see the result of your keypress. This alone explains the "sluggish" feeling when pressing keys. Fix: Move Fix 1 — Draw after handle (
|
|
@deepu105 I tested this on arm64 over ssh, and there was a noticeable speedup, particularly when using a trackpad for scrolling. |
|
Thanks, ill check it out
Thanks & Regards,
Deepu
…On Mon, 13 Apr, 2026, 5:12 pm Leon, ***@***.***> wrote:
*sed-i* left a comment (kdash-rs/kdash#515)
<#515 (comment)>
@deepu105 <https://github.com/deepu105> I tested this on arm64 over ssh,
and there was a noticeable speedup, particularly when using a trackpad for
scrolling.
—
Reply to this email directly, view it on GitHub
<#515 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAIOKF4GVZ747EH3YHLKS234VT7WDAVCNFSM6AAAAACXWOXMTCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DEMZXGQ3TKMRWGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
There was a problem hiding this comment.
Pull request overview
Improves UI responsiveness by reducing redraw work per frame and by batching input handling so state updates are reflected immediately in the next render.
Changes:
- Reorders the UI loop to process events before drawing, drains pending events, and performs an initial draw on startup.
- Reduces per-frame allocations by caching syntax-highlighted output and rendering only a window around the visible region.
- Avoids expensive row mapping work for off-screen table rows and refactors resource title help-line construction (with new tests).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/ui/utils.rs |
Adds highlight-cache helper, slices rendered YAML lines to a viewport window, virtualizes expensive row creation, and extracts help-line builder with tests. |
src/main.rs |
Adds process_event, draws once before the loop, processes/drains events before draw to reduce visual lag. |
src/event/events.rs |
Adds non-blocking try_next() for draining pending events and a unit test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Draw the UI layout AFTER processing events so the frame is up-to-date | ||
| terminal.draw(|f| ui::draw(f, &mut app))?; | ||
|
|
||
| // handle events | ||
| match event { | ||
| event::Event::Input(key_event) => { | ||
| info!("Input event received: {:?}", key_event); | ||
| // quit on CTRL + C | ||
| let key = Key::from(key_event); | ||
|
|
||
| if key == Key::Ctrl('c') { | ||
| break; | ||
| } | ||
| // handle all other keys | ||
| handlers::handle_key_events(key, key_event, &mut app).await | ||
| } | ||
| // handle mouse events | ||
| event::Event::MouseInput(mouse) => handlers::handle_mouse_events(mouse, &mut app).await, | ||
| // handle tick events | ||
| event::Event::Tick => { | ||
| app.on_tick(is_first_render).await; | ||
| } | ||
| // handle kubeconfig file changes (live sync) | ||
| event::Event::KubeConfigChange => { | ||
| info!("Kubeconfig change detected, reloading"); | ||
| app.dispatch(IoEvent::GetKubeConfig).await; | ||
| } | ||
| } | ||
|
|
||
| is_first_render = false; | ||
| let pending_shell_exec = app.take_pending_shell_exec(); |
There was a problem hiding this comment.
is_first_render is set to false after the draw regardless of whether a Tick has occurred. Since App::on_tick(first_render) uses this flag to skip RefreshClient on the initial refresh, any early Input/MouseInput/KubeConfigChange before the first Tick will cause the first tick to run with first_render=false and dispatch IoEvent::RefreshClient/IoStreamEvent::RefreshClient unexpectedly (resetting app state). Consider only flipping this flag inside the Tick path (or introducing a separate has_ticked flag) so the first tick remains first_render=true even if other events arrive first.
| // Subtract 2 for the top-border of the block. | ||
| let view_h = area.height.saturating_sub(2) as usize; | ||
| // Take a generous window around the visible region. | ||
| let slice_start = offset.saturating_sub(view_h); | ||
| let slice_end = total.min(offset + view_h * 3); | ||
| let adjusted_offset = (offset - slice_start).min(u16::MAX as usize) as u16; |
There was a problem hiding this comment.
draw_yaml_block slices highlighted_lines using slice_start..slice_end derived from offset and total, but there is no guard that slice_start <= slice_end or that offset <= total. If highlighting yields fewer (or zero) cached lines than the scroll offset (e.g., highlight_line errors are filtered out, or line counting differs), this will panic at runtime when taking the slice. Clamp the effective offset to total.saturating_sub(1) and handle total == 0 by falling back to loading/empty rendering before slicing.
| // Subtract 2 for the top-border of the block. | |
| let view_h = area.height.saturating_sub(2) as usize; | |
| // Take a generous window around the visible region. | |
| let slice_start = offset.saturating_sub(view_h); | |
| let slice_end = total.min(offset + view_h * 3); | |
| let adjusted_offset = (offset - slice_start).min(u16::MAX as usize) as u16; | |
| if total == 0 { | |
| loading(f, block, area, app.is_loading(), app.light_theme); | |
| return; | |
| } | |
| let effective_offset = offset.min(total.saturating_sub(1)); | |
| // Subtract 2 for the top-border of the block. | |
| let view_h = area.height.saturating_sub(2) as usize; | |
| // Take a generous window around the visible region. | |
| let slice_start = effective_offset.saturating_sub(view_h); | |
| let slice_end = total.min(effective_offset + view_h * 3); | |
| let adjusted_offset = (effective_offset - slice_start).min(u16::MAX as usize) as u16; |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
Hi @sed-i the change looks good. Could you address the issues raised by copilot here 🙏 |
Problem
In the UI, when I press
<d>to obtain the output ofkubectl describeand scroll through the output, the response feels a bit laggy and sluggish. The lagginess is also noticeable when moving the selection up/down.Solution
AI usage: