Skip to content

Commit 3d73fd3

Browse files
fix(core): make running a single task more transparent (#31163)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> Running a single task is in this uncanny framed state and doesn't exit immediately. <img width="1317" alt="image" src="https://github.com/user-attachments/assets/13e7463f-9eb0-48db-95f2-c09e203d494f" /> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Running a single task isn't framed and exits immediately. This also fixes scrolling in interactive mode based on whether or not the underlying terminal is in application cursor mode or not. ![image](https://github.com/user-attachments/assets/c32c85e7-edd6-4aba-9a15-bc75cc9ee5ba) ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: JamesHenry <[email protected]>
1 parent a312264 commit 3d73fd3

File tree

6 files changed

+197
-130
lines changed

6 files changed

+197
-130
lines changed

packages/nx/src/native/tui/app.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use color_eyre::eyre::Result;
2-
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEventKind};
2+
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};
33
use hashbrown::HashSet;
44
use napi::bindgen_prelude::External;
55
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
@@ -214,14 +214,22 @@ impl App {
214214
return;
215215
}
216216

217-
self.begin_exit_countdown()
217+
if self.tasks.len() > 1 {
218+
self.begin_exit_countdown()
219+
} else {
220+
self.quit();
221+
}
222+
}
223+
224+
fn quit(&mut self) {
225+
self.quit_at = Some(std::time::Instant::now());
218226
}
219227

220228
fn begin_exit_countdown(&mut self) {
221229
let countdown_duration = self.tui_config.auto_exit.countdown_seconds();
222230
// If countdown is disabled, exit immediately
223231
if countdown_duration.is_none() {
224-
self.quit_at = Some(std::time::Instant::now());
232+
self.quit();
225233
return;
226234
}
227235

@@ -708,27 +716,32 @@ impl App {
708716
self.user_has_interacted = true;
709717
}
710718

719+
if matches!(self.focus, Focus::MultipleOutput(_)) {
720+
self.handle_mouse_event(mouse).ok();
721+
return Ok(false);
722+
}
723+
711724
match mouse.kind {
712725
MouseEventKind::ScrollUp => {
713-
if matches!(self.focus, Focus::MultipleOutput(_)) {
726+
if matches!(self.focus, Focus::TaskList) {
727+
self.dispatch_action(Action::PreviousTask);
728+
} else {
714729
self.handle_key_event(KeyEvent::new(
715730
KeyCode::Up,
716731
KeyModifiers::empty(),
717732
))
718733
.ok();
719-
} else if matches!(self.focus, Focus::TaskList) {
720-
self.dispatch_action(Action::PreviousTask);
721734
}
722735
}
723736
MouseEventKind::ScrollDown => {
724-
if matches!(self.focus, Focus::MultipleOutput(_)) {
737+
if matches!(self.focus, Focus::TaskList) {
738+
self.dispatch_action(Action::NextTask);
739+
} else {
725740
self.handle_key_event(KeyEvent::new(
726741
KeyCode::Down,
727742
KeyModifiers::empty(),
728743
))
729744
.ok();
730-
} else if matches!(self.focus, Focus::TaskList) {
731-
self.dispatch_action(Action::NextTask);
732745
}
733746
}
734747
_ => {}
@@ -790,6 +803,7 @@ impl App {
790803
}
791804

792805
let frame_area = self.frame_area.unwrap();
806+
let tasks_list_hidden = self.is_task_list_hidden();
793807
let layout_areas = self.layout_areas.as_mut().unwrap();
794808

795809
if self.debug_mode {
@@ -970,6 +984,7 @@ impl App {
970984
);
971985

972986
let terminal_pane = TerminalPane::new()
987+
.minimal(tasks_list_hidden && self.tasks.len() == 1)
973988
.pty_data(terminal_pane_data)
974989
.continuous(task.continuous);
975990

@@ -1330,6 +1345,15 @@ impl App {
13301345
let _ = self.handle_pty_resize();
13311346
}
13321347

1348+
fn handle_mouse_event(&mut self, mouse_event: MouseEvent) -> io::Result<()> {
1349+
if let Focus::MultipleOutput(pane_idx) = self.focus {
1350+
let terminal_pane_data = &mut self.terminal_pane_data[pane_idx];
1351+
terminal_pane_data.handle_mouse_event(mouse_event)
1352+
} else {
1353+
Ok(())
1354+
}
1355+
}
1356+
13331357
/// Forward key events to the currently focused pane, if any.
13341358
fn handle_key_event(&mut self, key: KeyEvent) -> io::Result<()> {
13351359
if let Focus::MultipleOutput(pane_idx) = self.focus {

packages/nx/src/native/tui/components/layout_manager.rs

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl LayoutManager {
138138
self.mode = match self.mode {
139139
LayoutMode::Auto => {
140140
// If we are in auto mode, we need to figure out our current orientation and set the mode to the opposite.
141-
if self.is_vertical_layout_preferred(area.width, area.height, self.task_count) {
141+
if self.is_vertical_layout_preferred(area.width, area.height) {
142142
LayoutMode::Horizontal
143143
} else {
144144
LayoutMode::Vertical
@@ -249,9 +249,7 @@ impl LayoutManager {
249249
fn calculate_layout_visible_task_list(&self, area: Rect) -> LayoutAreas {
250250
// Determine whether to use vertical or horizontal layout
251251
let use_vertical = match self.mode {
252-
LayoutMode::Auto => {
253-
self.is_vertical_layout_preferred(area.width, area.height, self.task_count)
254-
}
252+
LayoutMode::Auto => self.is_vertical_layout_preferred(area.width, area.height),
255253
LayoutMode::Vertical => true,
256254
LayoutMode::Horizontal => false,
257255
};
@@ -401,17 +399,7 @@ impl LayoutManager {
401399
/// - Terminal aspect ratio
402400
/// - Number of tasks (single task prefers vertical layout)
403401
/// - Minimum dimensions requirements
404-
fn is_vertical_layout_preferred(
405-
&self,
406-
terminal_width: u16,
407-
terminal_height: u16,
408-
task_count: usize,
409-
) -> bool {
410-
// If there's only a single task, prefer vertical layout
411-
if task_count <= 1 {
412-
return true;
413-
}
414-
402+
fn is_vertical_layout_preferred(&self, terminal_width: u16, terminal_height: u16) -> bool {
415403
// Calculate aspect ratio (width/height)
416404
let aspect_ratio = terminal_width as f32 / terminal_height as f32;
417405

@@ -557,27 +545,6 @@ mod tests {
557545
assert_eq!(task_list.height, 100 / 3);
558546
}
559547

560-
#[test]
561-
fn test_auto_mode_prefers_vertical_for_single_task() {
562-
let mut layout_manager = LayoutManager::new(5);
563-
let area = create_test_area(200, 60); // Wide terminal that would normally use horizontal
564-
565-
layout_manager.set_mode(LayoutMode::Auto);
566-
layout_manager.set_task_list_visibility(TaskListVisibility::Visible);
567-
layout_manager.set_pane_arrangement(PaneArrangement::Single);
568-
layout_manager.set_task_count(1); // Single task
569-
570-
let layout = layout_manager.calculate_layout(area);
571-
assert!(layout.task_list.is_some());
572-
573-
// Even though terminal is wide, layout should be vertical for a single task
574-
let task_list = layout.task_list.unwrap();
575-
assert_eq!(task_list.x, 0);
576-
assert_eq!(task_list.y, 0);
577-
assert_eq!(task_list.width, 200);
578-
assert_eq!(task_list.height, 60 / 3);
579-
}
580-
581548
#[test]
582549
fn test_forced_vertical_mode() {
583550
let mut layout_manager = LayoutManager::new(5);
@@ -840,19 +807,6 @@ mod tests {
840807
insta::assert_snapshot!(terminal.backend());
841808
}
842809

843-
/// Visual test for auto mode with single task (should be vertical regardless of terminal size)
844-
#[test]
845-
fn test_visualize_auto_mode_single_task() {
846-
let mut layout_manager = LayoutManager::new(5);
847-
layout_manager.set_mode(LayoutMode::Auto);
848-
layout_manager.set_task_list_visibility(TaskListVisibility::Visible);
849-
layout_manager.set_pane_arrangement(PaneArrangement::Single);
850-
layout_manager.set_task_count(1);
851-
852-
let terminal = render_layout(120, 30, &layout_manager);
853-
insta::assert_snapshot!(terminal.backend());
854-
}
855-
856810
/// Visual test for auto mode with tall terminal
857811
#[test]
858812
fn test_visualize_auto_mode_tall_terminal() {

packages/nx/src/native/tui/components/snapshots/nx__native__tui__components__layout_manager__tests__visual_tests__visualize_auto_mode_single_task.snap

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)