Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions crates/cli/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16700,6 +16700,7 @@ fn playbook_cursor_at_modal_point(
scroll_offset: usize,
col: u16,
row: u16,
body_width: usize,
) -> Option<usize> {
let inner_x = modal
.x
Expand All @@ -16718,10 +16719,9 @@ fn playbook_cursor_at_modal_point(
// offset instead of being treated as a whole logical line.
let target_row = (row.saturating_sub(inner_y) as usize).saturating_add(scroll_offset);
let target_col = col.saturating_sub(inner_x) as usize;
let width = ui::playbook_modal_inner_width(modal);
Some(playbook_normalize_playbook_cursor(
buffer,
ui::playbook_visual_to_cursor(app, buffer, target_row, target_col, width),
ui::playbook_visual_to_cursor(app, buffer, target_row, target_col, body_width),
))
}

Expand Down Expand Up @@ -33123,6 +33123,42 @@ mod tests {
server.abort();
}

#[tokio::test]
async fn playbook_click_on_slid_popup_uses_unclipped_wrap_width() {
use crossterm::event::{MouseButton, MouseEvent, MouseEventKind};

let (mut app, _dir, server) = empty_app().await;
app.playbook_popup = Some(playbook_popup_for_test(
"s1",
"abcdefghijklmnopQRST",
0,
));

// A terminal-focused Playbook keeps its 20-column logical popup width
// while sliding four columns right, then clips at the pane edge. The
// body therefore paints at width 16 even though the clickable modal
// rectangle is only 16 columns wide (which would imply body width 12
// if used for layout). The first cell of the painted continuation row
// is source offset 16, not 12.
app.layout.modal_area = Some(Rect::new(4, 0, 16, 20));
app.layout.playbook_inner_area = Some(Rect::new(6, 2, 16, 16));

app.handle_playbook_mouse(&MouseEvent {
kind: MouseEventKind::Down(MouseButton::Left),
column: 6,
row: 3,
modifiers: KeyModifiers::NONE,
})
.await;

assert_eq!(
app.playbook_popup.as_ref().unwrap().cursor,
16,
"click mapping must use the full rendered body width, not the clipped hit rect"
);
server.abort();
}

#[tokio::test]
async fn playbook_click_maps_through_scroll_offset_on_wrapped_row() {
let (mut app, _dir, server) = empty_app().await;
Expand Down
21 changes: 21 additions & 0 deletions crates/cli/src/app/editor.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
use super::*;

impl App {
/// Width the playbook body was laid out at in the last frame.
///
/// A terminal-focused playbook slides right while retaining its full width,
/// but `modal_area` is clipped to the owning pane for hit-testing. Deriving
/// wrap width from that clipped rectangle makes click-to-caret resolve
/// against narrower lines than the ones on screen. The rendered inner area
/// keeps the logical width, so prefer it whenever it is available.
fn playbook_click_body_width(&self, modal: ratatui::layout::Rect) -> usize {
self.layout
.playbook_inner_area
.filter(|inner| inner.width > 0)
.map(|inner| inner.width as usize)
.unwrap_or_else(|| crate::ui::playbook_modal_inner_width(modal))
}

pub(super) fn place_playbook_cursor(
&mut self,
modal: ratatui::layout::Rect,
col: u16,
row: u16,
) {
let body_width = self.playbook_click_body_width(modal);
let cursor = {
let app: &App = self;
let Some(popup) = app.playbook_popup.as_ref() else {
Expand All @@ -19,6 +35,7 @@ impl App {
popup.scroll_offset,
col,
row,
body_width,
)
.unwrap_or(0)
};
Expand Down Expand Up @@ -514,6 +531,7 @@ impl App {
}
match ev.kind {
MouseEventKind::Down(MouseButton::Left) => {
let body_width = self.playbook_click_body_width(modal);
let cursor = {
let app: &App = self;
let Some(popup) = app.playbook_popup.as_ref() else {
Expand All @@ -526,6 +544,7 @@ impl App {
popup.scroll_offset,
ev.column,
modal_row,
body_width,
)
.unwrap_or(0)
};
Expand Down Expand Up @@ -567,6 +586,7 @@ impl App {
true
}
MouseEventKind::Drag(MouseButton::Left) => {
let body_width = self.playbook_click_body_width(modal);
let cursor = {
let app: &App = self;
let Some(popup) = app.playbook_popup.as_ref() else {
Expand All @@ -579,6 +599,7 @@ impl App {
popup.scroll_offset,
ev.column,
modal_row,
body_width,
)
.unwrap_or(0)
};
Expand Down
Loading