From 2379c2fdebfec5a970a41546fe79a43b837ed0db Mon Sep 17 00:00:00 2001 From: shuki Date: Fri, 6 Mar 2026 18:10:19 +0200 Subject: [PATCH] Add Kill Job button to Running Tasks screen Co-Authored-By: Claude Opus 4.6 --- tui/jobs.py | 10 ++++++++++ tui/screens/running_tasks.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tui/jobs.py b/tui/jobs.py index 7d8e24c..5280e5e 100644 --- a/tui/jobs.py +++ b/tui/jobs.py @@ -77,6 +77,16 @@ class JobManager: app.post_message(JobFinished(job.id, rc)) return job.return_code if job.return_code is not None else 1 + def kill_job(self, job_id: str) -> bool: + job = self._jobs.get(job_id) + if not job or job._proc is None: + return False + try: + job._proc.terminate() + return True + except ProcessLookupError: + return False + def kill_running(self) -> None: for job in self._jobs.values(): if job._proc is not None: diff --git a/tui/screens/running_tasks.py b/tui/screens/running_tasks.py index aa4ac64..636a453 100644 --- a/tui/screens/running_tasks.py +++ b/tui/screens/running_tasks.py @@ -6,7 +6,7 @@ from textual.widgets import Header, Footer, Static, Button, DataTable from textual.containers import Vertical, Horizontal from tui.jobs import job_manager -from tui.widgets import OperationLog +from tui.widgets import ConfirmDialog, OperationLog class RunningTasksScreen(Screen): @@ -20,6 +20,7 @@ class RunningTasksScreen(Screen): yield DataTable(id="rt-table") with Horizontal(id="rt-buttons"): yield Button("View Log", variant="primary", id="btn-rt-view") + yield Button("Kill Job", variant="error", id="btn-rt-kill") yield Button("Clear Finished", variant="warning", id="btn-rt-clear") yield Button("Back", id="btn-rt-back") yield Footer() @@ -66,6 +67,8 @@ class RunningTasksScreen(Screen): elif event.button.id == "btn-rt-clear": job_manager.remove_finished() self._refresh_table() + elif event.button.id == "btn-rt-kill": + self._kill_selected() elif event.button.id == "btn-rt-view": table = self.query_one("#rt-table", DataTable) if table.row_count == 0: @@ -82,5 +85,29 @@ class RunningTasksScreen(Screen): ) self.app.push_screen(log_screen) + def _kill_selected(self) -> None: + table = self.query_one("#rt-table", DataTable) + if table.row_count == 0: + self.notify("No jobs to kill", severity="warning") + return + row_key, _ = table.coordinate_to_cell_key(table.cursor_coordinate) + job_id = str(row_key) + job = job_manager.get_job(job_id) + if not job: + return + if job.status != "running": + self.notify("Job is not running", severity="warning") + return + self.app.push_screen( + ConfirmDialog(f"Kill job '{job.label}'?", "Confirm Kill"), + callback=lambda ok: self._do_kill(job_id) if ok else None, + ) + + def _do_kill(self, job_id: str) -> None: + if job_manager.kill_job(job_id): + self.notify("Job killed") + else: + self.notify("Could not kill job", severity="error") + def action_go_back(self) -> None: self.app.pop_screen()