feat(board): move cards to any column with digit keys and drag & drop#3522
Merged
Conversation
Reset drag on dialog open, key press, and stale-card refresh so a swallowed mouse-release can never silently move a card later. Drop resolves the card by ID (moveCard) rather than by current selection, bounds-check dragCol in renderFooter, ignore non-left releases, and suppress jitter-within-card so double-click attach still works. Assisted-By: Claude
docker-agent
reviewed
Jul 8, 2026
docker-agent
left a comment
Contributor
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Reviewed the drag-and-drop + digit-key card move implementation across pkg/board/tui/tui.go, pkg/board/tui/view.go, pkg/board/tui/dialogs.go, and pkg/board/tui/tui_test.go.
Summary: The implementation is sound. Key design decisions were validated:
moveCardbounds-checksdst(dst < 0 || dst >= len(m.columns)) — digit 1–9 and drag releases outside columns are safe no-ops.- Drag state invariant holds:
m.dragging = trueis only set inhandleMotionwhenm.dragCardID != ""(guarded at line 745), sohandleReleasecan never callmoveCardwith an empty card ID. msg.String()[0]inMoveTois safe: bubbletea guarantees a single-character string for single-character key bindings, and the path is only reached afterkey.Matchessucceeds for one of "1"–"9".- Drag state cleared correctly on dialog open, key press, and board refresh — the re-validation in
reload()catches any swallowed mouse-release. - Drop resolves by card ID, not position — a refresh mid-drag cannot redirect to the wrong card.
renderFooterbounds-checksm.dragColbefore indexingm.columns.- Non-left-button releases ignored in
handleRelease(line 767).
No bugs introduced by this PR were found.
Sayt-0
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Kanban board previously only let you step a card one column at a time with
[and]. For boards with many columns this is tedious, and there was no way to reposition a card at all without a keyboard.This PR adds two complementary shortcuts. Pressing a digit key 1–9 moves the selected card directly to that column number (
MoveTobinding), while[/]continue to do single-step moves. Moving a card forward still triggers the destination column's prompt via the existingApp.MoveCardengine path, so the workflow is unchanged. For mouse users, pressing on a card and dragging it over another column highlights that column's border in green and shows a "Release to move the card to <column>" hint in the footer; releasing the button completes the move. Drops on the origin column or outside the board are no-ops, and motion jitter within the pressed cell is treated as a click rather than a drag.A second commit hardens the edge cases surfaced during review: drag state is cleared whenever a dialog opens or a key is pressed, and it is re-validated on every board refresh so a swallowed mouse-release cannot leave stale state that fires on a later click. The drop resolves the target by card ID rather than position, so a refresh mid-drag cannot move the wrong card. Non-left-button releases are ignored, and
renderFooterbounds-checks the drop-target column index before accessing it.