# Clickable Footer Shortcuts Plan

Plan for making the bottom shortcut bar clickable, adding the missing visible `A` purge shortcut, making confirmation `y`/`n` clickable, and hiding lower-priority shortcuts on narrow terminals.

ctop TUI src/_core.js review artifact 2026-06-06

Recommendation: implement a single shortcut descriptor list and use it for footer rendering, click hitbox registration, command-palette entries, and tests where practical. Footer clicks should dispatch the same keys that keyboard input already handles, so click behavior stays identical to existing shortcuts.

## Interactive Review Mock

This browser mock is not the TUI implementation. It demonstrates the proposed priority behavior and confirmation click states.

ctop - clickable footer prototype
CTOP AI Agent Terminal Operations Panel
2 active | 1 stopped | sort age | ctx ok | notif on
PID STATUS CTX STARTED TITLE CPU%
8412 ACTIVE 82%4m ago feature-clickable-footer 2.4
8270 ACTIVE 61%19m ago docs-polish 1.8
7921 STOPPED--2h ago old-session 0.0
Kill ALL stopped/dead processes?
KEYS:
Click a shortcut in the mock footer.

## Current State

### What already exists

- Mouse tracking is enabled and routed through `parseMouseEvent` then `handleMouseEvent`.
- Rows can already be clicked and scroll events already navigate the list.
- The keyboard path already supports `A` for killing stopped/dead processes with confirmation.
- The help screen already lists `A`; the footer does not.

### Gaps to close

- The footer is rendered as static text with no recorded hitboxes.
- Confirmation prompts are written directly to stdout, so their `y`/`N` locations are not clickable.
- Footer content is a long hardcoded line and can truncate unpredictably on narrow terminals.
- Shortcut labels are duplicated between the footer, command palette, help, and keyboard switch.

## Architecture

Current: renderNow() -> hardcoded footer string mouse event -> handleMouseEvent() -> row hit test only keyboard event -> handleInput(key) -> switch(key) Target: SHORTCUTS metadata -> renderFooterShortcuts(columns) -> visible shortcut text -> footerHitboxes -> command palette metadata -> tests mouse event -> handleMouseEvent() -> confirmation hitbox? -> footer hitbox? -> row hit test fallback -> handleInput(shortcut.key)

### New helper responsibilities

Helper | Purpose | Notes
--- | --- | ---
`SHORTCUTS` | Canonical footer shortcut metadata. | Key, label, command key, priority, danger flag, visibility predicate.
`renderFooterShortcuts(columns)` | Builds the footer line and records clickable ranges. | Always keeps `?`. Adds `A`. Hides by priority as width shrinks.
`findFooterShortcutAt(row, col)` | Maps a mouse click to a shortcut key. | Uses measured visual columns, not string length with ANSI escapes.
`renderConfirmPrompt(type)` | Renders confirmation prompts and records `y`/`n` hitboxes. | Can still accept keyboard `y` and any other key as cancel.
`findConfirmChoiceAt(row, col)` | Maps prompt click to `y` or `n`. | Runs before footer and row hit testing.

## Shortcut Priority

Priority should be explicit. Lower numbers stay visible longer. The renderer should add shortcuts in priority order, reserve room for `KEYS:` and `?`, then stop once the next item would exceed terminal width.

Priority | Shortcut | Reason | Visibility
--- | --- | --- | ---
0 | `?` Help | Escape hatch requested to always remain visible. | Always shown, even on tiny width.
1 | `jk` Nav, `x` Kill, `X` Force | Core operational controls. | Shown first after mandatory help.
2 | `A` Purge, `K` Kill All | Danger actions need discoverability, but remain confirmed. | Shown before convenience navigation.
3 | `/` Filter, `F` Search, `s` Sort, `r` Refresh | Frequent list operations. | Shown on normal widths.
4 | `o` Open, `G` Group, `L` Log, `W` Timeline, `E` Export | Useful but not needed every moment. | Hidden earlier on medium widths.
5 | `T` Theme, `d` Dash, `H` History, `C` Heatmap, `n` Notif, `P` Pane, `q` Quit | Lower urgency or already discoverable through help. | First to hide on narrow widths.

## Implementation Plan

1. Introduce footer shortcut metadata. Add a `FOOTER_SHORTCUTS` array near the command metadata in `src/_core.js`. Include `A Purge` and optionally `K All` so all destructive footer shortcuts are visible when width allows.
2. Render footer from metadata. Replace the hardcoded footer lines in `renderNow()` with `renderFooterShortcuts(columns, rows)`. The helper returns ANSI-rendered text and updates a module-level `footerHitboxes` array.
3. Measure click ranges in terminal columns. Track the start and end column for each shortcut token while building the plain footer text. Make the whole label clickable, not only the key character, so `x Kill` and `F Search` feel easy to hit.
4. Dispatch footer clicks through keyboard handling. In `handleMouseEvent`, before row hit testing, check whether the click is on the footer row. If so, call `handleInput(hit.key)` and return.
5. Move confirmation prompts into render state. Replace direct `process.stdout.write(... (y/N))` confirmation output with a small confirmation state object, for example `confirmPrompt = { type, message }`. Render it inside `renderNow()` or through a focused prompt render helper that records `confirmHitboxes`.
6. Make `y` and `n` clickable. Let confirmation click hitboxes dispatch `handleInput('y')` or `handleInput('n')`. Keep current keyboard behavior where `y`/`Y` confirms and other keys cancel.
7. Export small pure helpers for tests. Export the footer selection and hitbox helpers, or expose them through existing test internals. Avoid making tests depend on full-screen rendering side effects.
8. Update docs. Add `A` to README keyboard shortcuts if it should be user-facing there too. The help text already includes it.

## Detailed Design

### Footer descriptor shape

{ key: "A", label: "Purge", priority: 2, danger: true, always: false, enabledWhen: () => true }

### Hitbox shape

{ row: process.stdout.rows, colStart: 31, colEnd: 39, key: "A", kind: "footer-shortcut" }

### Width behavior

The algorithm should not rely on breakpoint-specific hardcoded strings. It should greedily fit items by priority after reserving space for the mandatory help item. This keeps behavior stable across terminal widths and label edits.

renderFooterShortcuts(columns): mandatory = [? Help] candidates = shortcuts excluding mandatory, sorted by priority then order visible = [] for candidate in candidates: if fits("KEYS:" + visible + candidate + mandatory): visible.push(candidate) return render("KEYS:" + visible + mandatory)

### Confirmation behavior

The current flags can remain, but direct prompt writes should become renderable state. This avoids stale prompts and gives mouse handling a stable row/column model.

beginConfirmation("purge-stopped"): confirmKillStopped = true confirmPrompt = { message: "Kill ALL 3 stopped/dead processes?", yesKey: "y", noKey: "n" } render()

## Files To Modify

File | Change | Reason
--- | --- | ---
`src/_core.js` | Modify | Add shortcut metadata, footer renderer, hitbox lookup, confirmation prompt rendering, and mouse dispatch.
`src/input.js` | Maybe modify | Only if new pure helpers should be re-exported from the input facade.
`test/mouse.test.js` | Modify | Add footer hitbox mapping, priority hiding, and confirmation click tests.
`test/status-bar.test.js` or new `test/footer-shortcuts.test.js` | Modify or add | Keep render and width-fit tests focused.
`README.md` | Modify | Add `A` purge stopped/dead sessions to the public shortcut table.

## Validation Plan

### Unit tests

- `?` appears at all widths that can render any footer.
- `A Purge` appears at normal width and maps to key `A`.
- Lower-priority items disappear before higher-priority items.
- Hitbox lookup ignores ANSI escape lengths.

### Mouse tests

- Clicking footer `r Refresh` dispatches `r`.
- Clicking footer `A Purge` enters stopped/dead confirmation when stopped processes exist.
- Footer clicks do not select list rows.
- Mouse release events still do nothing.

### Confirmation tests

- Clicking `y` executes the same path as pressing `y`.
- Clicking `n` cancels the prompt.
- Keyboard confirmation still works for `K`, `A`, and bulk `x`/`X`.
- Prompt hitboxes clear after resolution.

Final command: `npm test`. Manual smoke check: run `npm start`, click footer shortcuts in a terminal with mouse reporting, resize to narrow widths, and confirm `?` remains visible.

## Risks And Decisions

Risk | Decision
--- | ---
Directly calling `handleInput` from mouse dispatch can recurse if mouse sequences are passed through. | Only dispatch plain shortcut keys from hitboxes, never raw mouse escape sequences.
Terminal column width differs from JavaScript string length with ANSI codes. | Track hitboxes from plain labels while rendering ANSI separately.
Destructive actions become easier to trigger by click. | Keep confirmation prompts for `K`, `A`, and bulk kill paths. Make `n` clickable and keep non-yes keys as cancel.
Hidden shortcuts could reduce discoverability on narrow terminals. | Always keep `?` visible and route users to the full help screen.

## Source Notes

Observed in the current code: command palette metadata at `src/_core.js:614`, static footer rendering at `src/_core.js:4810`, row-only mouse click fallback at `src/_core.js:5739`, confirmation handling at `src/_core.js:5931`, and existing `A` keyboard handling at `src/_core.js:6327`.