Files
Gokul 14d30b6b57 feat(ui): add job selection mode to TUI for running individual jobs (#80)
* feat(ui): add job selection mode to TUI for running individual jobs

The CLI already has --job to run a single job, but the TUI had no
way to do this. You could only run entire workflows, which is the
kind of all-or-nothing approach that gets old fast when you have a
workflow with 12 jobs and you just want to re-run "lint".

Add a job selection sub-view to the Workflows tab. Press Enter on a
workflow to drill into its jobs (parsed via parse_workflow), then
Enter on a job to run just that one (with its transitive deps), or
'a' to run all, or Esc to go back. The selected job flows through
as target_job in ExecutionConfig, reusing the exact same filtering
logic the CLI --job flag already uses.

While at it, updated the status bar hints and help overlay to
document the new keybindings.

* fix(ui): stop target_job from leaking across queued workflows

The job selection feature added in 8406c60 stored target_job as a
single global field on App, shared across all queued executions.
It turns out that when you use a global mutable field as a
communication channel between "user picked a job" and "executor
should run this job", *any* workflow queued after the first one
silently inherits whatever target_job was set. Confusion ensues.

While at it, the Enter key was repurposed from "run this workflow"
to "enter job selection mode", which means the most common operation
now takes two keypresses instead of one. That's not great.

Also, pressing 'r' during job selection mode would bypass it
entirely and start execution with target_job still set from the
previous selection, leaving the UI in a corrupt state. Please don't
ship unguarded key handlers in modal UIs.

The fix:

- Replace the global target_job with a per-entry QueuedExecution
  struct that carries its own target_job. Each queued workflow now
  owns its execution config. No more ambient mutable state.

- Cache parsed job names in Workflow at discovery time instead of
  re-parsing the YAML file on every Enter keypress.

- Restore Enter to its original "run the workflow" behavior. Job
  selection is now Shift+J, which doesn't steal the primary action
  key.

- Guard 'r' and other keys properly in job_selection_mode.

- Deduplicate select_job_and_run/run_all_jobs into a single
  run_from_job_selection(target: Option<String>) method.

* fix(ui): fix job selection bugs and add tests

The single-file CLI path (`wrkflw --tui <file>`) was hardcoding
job_names to Vec::new(), which means Shift+J would always report
"No jobs found" even when the workflow *obviously* has jobs. Not
great when the whole point of this feature is selecting jobs.

The dedup logic in run_from_job_selection() was checking
workflow_idx equality only, so queueing the same workflow with
different target jobs (run "build", then run "test") would silently
drop the second one. The user clicks a thing, nothing happens.
Confusion ensues.

While at it, extract the job name parsing into a shared
extract_job_names() helper — the same six lines were copy-pasted
in three places, which is exactly how you end up with three
slightly different bugs later.

Add 12 unit tests covering the job selection state machine:
enter/exit mode, navigation wrapping, target_job threading through
the execution queue, and the dedup fix.

* fix(ui): deduplicate job selection cleanup and add edge case tests

run_from_job_selection was manually inlining the same three lines
that exit_job_selection_mode already does. That's the kind of thing
that *will* drift the moment someone adds cleanup logic to one path
but not the other. Let's just call the existing method.

While at it, add tests for two untested edge cases: calling
run_from_job_selection when no workflow is selected (should be a
safe no-op), and entering job selection mode with an out-of-bounds
index (should also be a no-op). Also document the precondition that
callers must check !self.running before calling run_from_job_selection.
2026-04-02 15:38:58 +05:30
..

wrkflw-ui

Terminal user interface for browsing workflows, running them, and viewing logs.

  • Tabs: Workflows, Execution, Logs, Help
  • Hotkeys: 1-4, Tab, Enter, r, R, t, v, e, q, etc.
  • Integrates with wrkflw-executor and wrkflw-logging

Example

use std::path::PathBuf;
use wrkflw_executor::RuntimeType;
use wrkflw_ui::run_wrkflw_tui;

# tokio_test::block_on(async {
let path = PathBuf::from(".github/workflows");
run_wrkflw_tui(Some(&path), RuntimeType::Docker, true, false).await?;
# Ok::<_, Box<dyn std::error::Error>>(())
# })?;

Most users should run the wrkflw binary and select TUI mode: wrkflw tui.