fix(tabs): a found tab still drops onto the tile grid during a rail search (#580)

183efacc turned drag off while the rail search narrows the list, as the
owner asked, by refusing the flat manual rail's dragstart. That also
refused a drag that never reorders anything: with the tile grid open, a
found tab dragged onto a tile or an empty cell set no draggedTabId, so
_acceptTabDrops() (tile-grid.js) saw nothing and the drop was lost. On
the PR head that drop replaced or swapped the tile. The owner's reason
covers the reorder only: a reorder drop is saved for every device,
relative to rows the search hides, while the tile grid is per-device and
lands beside nothing hidden.

dragstart is unconditional again. The rows refuse the reorder instead:
their dragover returns before preventDefault while _tabRailSearchActive()
(the browser shows no-drop and fires no drop there), and their drop
returns before touching sessionOrder, for anything above the row that
might let a drop through. `draggable` stays on, so a cleared search
reorders with the same rows. The grouped rail's pointer drag keeps its
refusal in _onTabLayoutPointerDown(): it has no drop target besides the
rail.

Test: during a search that leaves both rows showing, dragstart is not
cancelled and sets draggedTabId, dragover on another row is not
accepted, a drop there leaves sessionOrder alone and saves nothing, the
real _acceptTabDrops() binder accepts the drag on a tile and hands it
the id, and after clearing the same rows reorder. It fails on the
previous commit, without the dragover guard, and without the drop guard.
CLAUDE.md, the invariants, the Dashboard wiki row and the release
changeset now say reordering by drag is off and a tile drop still works.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-10-10 04:45:35 +02:00
1 parent 6844cccd18
commit f152d551d1
6 files changed
+77 -31

No files matched your search

+10 -8
View File
@@ -8240,14 +8240,6 @@ class CodemanApp {
tab.setAttribute('draggable', 'true');
tab.addEventListener('dragstart', (e) => {
// No drag while the rail search narrows the list: the drop rewrites the
// order every device sees, relative to rows the search is hiding
// (owner's call on #580). Refused here rather than by `draggable`,
// because a keystroke in the box does not re-render the rows.
if (this._tabRailSearchActive()) {
e.preventDefault();
return;
}
this.draggedTabId = tab.dataset.id;
tab.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
@@ -8264,6 +8256,13 @@ class CodemanApp {
});
tab.addEventListener('dragover', (e) => {
// No reorder while the rail search narrows the list: the drop rewrites
// the order every device sees, relative to rows the search is hiding
// (owner's call on #580). The rows refuse it here, never in dragstart
// or via `draggable`: a found tab can still be dropped onto the tile
// grid (per-device, nothing hidden to land beside), and a keystroke in
// the box does not re-render the rows.
if (this._tabRailSearchActive()) return;
// Grouped by state: a tab in another group is not a drop target, and
// leaving the event alone (no preventDefault) is what shows "no drop".
if (this._isTabDropAcrossGroups(tab)) return;
@@ -8295,6 +8294,9 @@ class CodemanApp {
e.preventDefault();
tab.classList.remove('drag-over-left', 'drag-over-right');
// dragover refuses it during a rail search; this holds should anything
// above the row accept the drag and let a drop through.
if (this._tabRailSearchActive()) return;
if (!this.draggedTabId || this.draggedTabId === tab.dataset.id) return;
if (this._isTabDropAcrossGroups(tab)) return;