Skip to content

Pinned task quick-nav in the detail view (filter-aware)#630

Open
bborn wants to merge 4 commits into
mainfrom
task/4652-better-task-details-pinned-ux
Open

Pinned task quick-nav in the detail view (filter-aware)#630
bborn wants to merge 4 commits into
mainfrom
task/4652-better-task-details-pinned-ux

Conversation

@bborn

@bborn bborn commented Jul 7, 2026

Copy link
Copy Markdown
Owner

What & why

When you're heads-down on a few tasks, the detail view made you bounce back to the board to switch between them. This adds a pinned quick-nav pill bar at the top of the task detail view so you can hop between the tasks you're currently focused on — your pinned tasks — without leaving the view.

It respects the board's active filter: if the kanban is filtered to [projectX], the bar only shows projectX's pinned tasks, not every pin across the board.

The bar looks like:

📌  1·#7 Better task detai…   2·#42 Refactor login fl…   3·#108 Fix executor mixup   4·#200 Docs pass

The current task's pill is highlighted. With many pins the bar windows around the current one (‹N / N› markers) so the active task is always visible and the line never wraps.

How to use

In the detail view:

  • [ / ] — previous / next pinned task (wraps around)
  • 19 — jump directly to that pill

Switching is instant and reuses the existing prev/next transition path.

The "multiple ty instances mix up executor windows" bug

This was already fixed on main by the granular per-task executor flock (d733659c): a second ty opening the same task's executor is refused (shown as "running in another ty instance") while different tasks coexist freely — exactly the requested behavior. This PR deliberately routes pinned-nav task-switching through that same lock/cleanup path (so rapidly hopping between pins never fights over a pane) and adds regression coverage. No lock changes were required.

Implementation notes

  • internal/ui/detail_pinned_nav.goPinnedNavItem, SetPinnedNav, the windowed pill renderer, and next/prev/nth-jump helpers. The bar renders inside renderHeader() so viewSignature captures it and the render cache stays correct; headerHeight() grows by one line when the bar shows so the viewport reflows.
  • internal/ui/detail.go — header/viewport wiring; help hint ([/] pinned).
  • internal/ui/app.gofilteredPinnedTasks() reuses the board's scoreTaskForFilter predicate; jumpToPinnedTask(); [/] + digit handling in updateDetail; nav refreshed on task open and on task-set changes (pin/unpin). New PrevPinnedTask/NextPinnedTask keybindings.

Tests

  • Pinned-nav visibility, next/prev wrap, 1-9 quick-jump, header rendering + headerHeight.
  • filteredPinnedTasks respects the filter, including [project] bracket syntax.
  • Integration test driving the real updateDetail keypress path (] and a digit both begin the transition + return a loadTask command).

go test ./internal/ui/… green, golangci-lint run (v2.8.0, matching CI) clean, gofmt clean.

🤖 Generated with Claude Code

Add a compact pill bar at the top of the task detail view for hopping
between the tasks you're currently focused on — your pinned tasks —
without leaving the detail view. It respects the board's active filter,
so a board filtered to "[projectX]" surfaces only projectX's pins, not
every pin across the board.

- New DetailModel pinned quick-nav: PinnedNavItem + SetPinnedNav, a
  windowed single-line pill bar rendered in the header (so viewSignature
  captures it and the render cache stays correct). The bar hides when
  there's nowhere to hop (no pins, or the only pin is the current task),
  and grows headerHeight by one line when shown so the viewport reflows.
- Keys in the detail view: [ / ] cycle prev/next pinned task (wrapping),
  1-9 jump directly to that pill. Navigation reuses the existing
  prev/next transition path (CleanupWithoutSaving + loadTask under the
  taskTransitionInProgress guard), which already releases and
  re-acquires the per-task executor flock — so rapidly switching pinned
  tasks never fights over an executor pane.
- AppModel.filteredPinnedTasks applies the same fuzzy filter predicate
  the board uses (scoreTaskForFilter), ordered by status column then ID.
  The bar is refreshed on task open and on task-set changes (pin/unpin).

The reported "multiple ty instances mixing up executor windows" is
already fixed on main by the granular per-task executor flock
(d733659): a second instance opening the SAME task's executor is
refused (shown as busy) while different tasks coexist freely. This
change deliberately routes pinned-nav switching through that same lock
path and adds coverage; no lock changes were needed.

Tests: pinned-nav visibility/wrap/quick-jump, header rendering +
headerHeight, filter-respect (incl. [project] bracket syntax), and an
integration test driving the real updateDetail keypress path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bborn

bborn commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

QA evidence — real TUI screenshots

Rendered from the actual ty TUI (VHS headless terminal, seeded isolated instance — two projects webapp/api, pinned tasks spread across columns), not a harness/component render.

1. Pinned quick-nav bar in the detail view (unfiltered)

All 5 pinned tasks show as numbered pills, the current one (#1) highlighted, ordered by status column. Note the [/] pinned hint in the footer.

pinned-nav-all

2. Same view with the board filtered to [webapp]

The bar now lists only the 3 webapp pins — the two api pins (#5, #6) are correctly excluded. This is the ticket's core requirement: the detail-view nav honours the board's active filter.

pinned-nav-filtered

Interactions verified live (via --debug-state-file)

Driving the real TUI (not just unit tests):

  • ] from #6 → advances to next pinned #2
  • 4 → jumps directly to pill 4 (#5)
  • Applying [webapp] then opening a task → nav shows only #1 #2 #3

All green: go test ./internal/ui/…, golangci-lint v2.8.0, gofmt.

Per design feedback, render the pinned quick-nav pill bar at the bottom
of the detail view (just above the help footer) instead of the top, so
the top stays clean (status/badges). The reserved chrome line already
accounted for by headerHeight() is unchanged — only the render position
moves — and the bar is now folded into viewSignature directly (it's no
longer part of the header string).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bborn

bborn commented Jul 7, 2026

Copy link
Copy Markdown
Owner Author

Updated per feedback — pinned bar moved to the bottom

Moved the pinned quick-nav pill bar from the top to the bottom of the detail box (just above the help footer), so the top stays clean (status/badges). Commit a9a409b3.

Unfiltered — all 5 pins at the bottom, current (#1) highlighted

pinned-nav-all-bottom

Filtered to [webapp] — only the 3 webapp pins

pinned-nav-filtered-bottom

(Rendered from the real TUI; [ / ] cycle and 1-9 jump verified live. Tests, golangci-lint v2.8.0, and gofmt all green.)

bborn and others added 2 commits July 7, 2026 14:45
The new PrevPinnedTask/NextPinnedTask keymap actions have no desktop GUI
equivalent, so the multi-surface parity gate (internal/parity) failed.
They are keyboard-only affordances ([ / ]) for cycling pinned tasks in
the detail view; the desktop GUI switches tasks via its own list UI and
already honors the board filter. Record them as deliberate skips in
parity-ignore.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-details-pinned-ux

# Conflicts:
#	parity-ignore.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant