Skip to content

BACK-548 - Add quick toggles for hiding empty board columns (TUI hotkey + browser board button) - #808

Open
janosmiko wants to merge 9 commits into
MrLesk:mainfrom
janosmiko:tasks/back-548-hide-empty-columns
Open

BACK-548 - Add quick toggles for hiding empty board columns (TUI hotkey + browser board button)#808
janosmiko wants to merge 9 commits into
MrLesk:mainfrom
janosmiko:tasks/back-548-hide-empty-columns

Conversation

@janosmiko

@janosmiko janosmiko commented Jul 23, 2026

Copy link
Copy Markdown

Summary

Adds quick toggles for the existing hideEmptyColumns setting so users can flip it from the surfaces where they actually look at the board, instead of only from the browser Settings page (shipped in BACK-522).

  • TUI: H (Shift-h) hotkey on the board view filters empty status columns at the render boundary via a pure filterVisibleColumns(data, hideEmptyColumns, isMoving) helper. Move mode always sees all columns (mirrors the web isDragging bypass). The flag persists to config.yml with an in-flight guard against key-repeat races and reverts on save failure.
  • Web: a toggle button in the board toolbar with aria-pressed state and optimistic update (reverts on API failure), threaded App -> BoardPage -> Board. Empty columns still reappear as drop targets while dragging.
  • Both toggles operate on the same shared hideEmptyColumns semantics so surfaces stay consistent.

Drag-and-drop hardening (web)

Re-showing hidden empty columns during a drag needed three fixes to work with native HTML5 drag-and-drop, especially on boards with many statuses:

  • Deferred expansion. Re-showing columns synchronously inside dragstart mutates the board layout, which cancels the native drag in Chromium (cards became undraggable with the toggle on). The expansion is now deferred to the next task with setTimeout(0) so the browser commits the drag first; empty columns still reappear as drop targets once the drag is active.
  • Keep the dragged column in view. Expanding inserts columns to the left of the dragged one, so the board jumped back to the leftmost columns. Its viewport position is captured at dragstart and scrollLeft restored in a layout effect after expansion, so the source column stays put.
  • Edge auto-scroll. Native DnD does not auto-scroll, so off-screen columns were unreachable mid-drag. Dragging near the left/right edge now scrolls the board horizontally so any column can be reached.

Docs

  • TUI footer and help popup document the H hotkey.
  • README Web Interface section mentions both toggles.

Tests

  • src/test/board-hide-empty-columns.test.ts - pure-function tests for filterVisibleColumns (including move-mode bypass).
  • src/test/web-board-hide-empty-columns.test.tsx - JSDOM component tests for the browser button (state, label, click handler, absent without handler) plus a regression test that dragstart does not re-show hidden columns synchronously (only after the drag commits).

Note: JSDOM has no layout engine, so scroll-preservation and edge auto-scroll were verified manually in a browser rather than in the test suite.

Verification

  • bunx tsc --noEmit - clean
  • bun run check . (Biome) - clean
  • bun test - green (the hide-empty-columns tests plus full suite)

Note: two pre-existing failures in tui-task-composer.test.ts (git-hook cases) reproduce on a clean main checkout and are unrelated to this change.

## Summary

- add an H hotkey on the TUI board that filters empty status columns at
the render boundary via a pure filterVisibleColumns helper, with move
mode always seeing all columns
- add a toggle button to the browser board toolbar with aria-pressed
state and optimistic update that reverts on API failure
- persist both toggles to the same hideEmptyColumns config key so the
surfaces stay consistent, guarded against key-repeat races
- document the hotkey in the TUI footer and help popup, and both toggles
in the README

## Verification

tsc exit 0, Biome clean across 333 files, bun test 1713 pass / 4 skip /
0 fail. Live TUI check confirmed empty columns hide and restore with the
config round-tripping true/false; 8 new unit and component tests cover
the filter helper, move-mode bypass, button state, and toggle handler.
# Conflicts:
#	src/ui/board.ts
#	src/ui/unified-view.ts
Re-showing hidden empty columns ran synchronously inside dragstart, which
mutates the board layout and cancels the native HTML5 drag in Chromium, so
cards became undraggable while the toggle was on. Defer the column expansion to
the next task with setTimeout(0) so the browser commits the drag first; empty
columns still reappear as drop targets once the drag is active.
…ring drag

With many statuses, expanding hidden columns on drag inserted columns to the
left of the dragged one, so the board jumped back to the leftmost columns and
the source column (and its off-screen drop targets) became unreachable.

- Pin the dragged column: capture its viewport position at dragstart and restore
  scrollLeft in a layout effect after the columns expand, so it stays put.
- Auto-scroll horizontally while dragging near the left/right board edge so
  off-screen columns become reachable (native HTML5 DnD does not auto-scroll).
The shipped composer did not work around neo-neo-bblessed defects (verified
against the installed library):
- textbox backspace deletes then returns before the trailing render (no repaint)
- textarea backspace/delete branches are empty under fullUnicode screens (no-op)
- list hardcodes selected=0 and ignores the selected option, so the Status
  picker always opened on Draft
- list binds j/k only under vi, which filter-popup never set

Fixes:
- filter-popup: picker.select(selectedIndex) + vi:true (also fixes the board's
  own single-select filter popups' preselection and j/k navigation)
- task-composer: ignoreKeys backspace/delete on the Title textbox and own
  deletion + render via deleteLastChar/deleteLastWord; Ctrl+W deletes the
  previous word in both inputs
- resting Status resolves via getDefaultCreateStatus (exported from
  task-wizard), matching the CLI wizard
Focus was shown only by a gray/yellow border, which is too subtle in the full
layout where the Status/Type/Priority/Create/Cancel controls have no cursor.
Invert the active control in both layouts, and highlight the label of the
active text input (keeping typed text readable) so tab/shift+tab focus is
obvious.
The active state appended bg-stone-700/text-white on top of the base
bg-white/text-gray-700. Tailwind resolves conflicting utilities by
stylesheet order, not class order, and .bg-white/.text-white are emitted
last, so the toggled-on button rendered white text on a white background
in light mode.

Split the shared structural classes out of the color classes so the
active and default states each carry one complete, non-overlapping set.
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