From f152d551d1633826aa73c5c52f98c46df6fc8a31 Mon Sep 17 00:00:00 2001 From: Codeman maintainer Date: Sat, 10 Oct 2026 04:40:13 +0200 Subject: [PATCH] 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) --- .changeset/release-1-41-0.md | 2 +- CLAUDE.md | 2 +- docs/architecture-invariants.md | 2 +- docs/wiki/The-Dashboard.md | 2 +- src/web/public/app.js | 18 ++++---- test/tab-rail-search.test.ts | 82 +++++++++++++++++++++++++-------- 6 files changed, 77 insertions(+), 31 deletions(-) diff --git a/.changeset/release-1-41-0.md b/.changeset/release-1-41-0.md index f6a28f37..43ef8b8a 100644 --- a/.changeset/release-1-41-0.md +++ b/.changeset/release-1-41-0.md @@ -25,7 +25,7 @@ **CLI logos on tabs can be switched off (#569).** App Settings → Appearance → Tabs → **CLI Logos on Tabs** hides the agent logo on every tab surface (header strip, rails, sidebar, phone chips, the desktop home list) on this device. On by default. Tile headers, split pane headers and the Run menus keep their logos. -**Search sessions on the vertical rail (#580).** The vertical tab rail has a **Search sessions** box at the top: type part of a name and the rail narrows to the tabs that match (a web tab by its title), across every group, collapsed ones included, without touching your groups, their collapse or the tab order. A tab with an alert stays visible even when its name does not match, so a prompt waiting on you is never filtered away. Escape or × clears it, dragging is off while it is active, and nothing is saved. The sidebar's filter box shares the same filter: a tab with an alert stays visible there too, and in the by-case tab layout a case with no match now hides. +**Search sessions on the vertical rail (#580).** The vertical tab rail has a **Search sessions** box at the top: type part of a name and the rail narrows to the tabs that match (a web tab by its title), across every group, collapsed ones included, without touching your groups, their collapse or the tab order. A tab with an alert stays visible even when its name does not match, so a prompt waiting on you is never filtered away. Escape or × clears it, reordering by drag is off while it is active (on a manual rail a found tab can still be dragged onto the tile grid), and nothing is saved. The sidebar's filter box shares the same filter: a tab with an alert stays visible there too, and in the by-case tab layout a case with no match now hides. **Fixes.** - **Clone errors on non-English hosts (#572, from #568).** Cloning a repository as a case now classifies a failed clone correctly whatever the host's language: a missing branch or tag is "does not exist on the remote" (400) and a missing repository is a 404, instead of a generic 422 with git's German (or any other) error text. Git runs with `LC_ALL=C` for clones and repo status, so the repo status card's error text is English on every host as well. diff --git a/CLAUDE.md b/CLAUDE.md index be353a7c..338e9f7b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -337,7 +337,7 @@ Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. L **Mobile tab strip scrolling** (issue #257): under 768px the tab strip scrolls horizontally, so the active tab must be kept reachable. `_updateActiveTabImmediate()` reveals it via `computeTabScrollLeft()` (constants.js, rect math on the strip's own `scrollLeft`, never `scrollIntoView()`, which scrolls the document under the fixed header); `_fullRenderSessionTabs()` must restore `scrollLeft` across rebuilds and re-reveal only when the active tab changed (`_lastRenderedActiveTabId`) or, grouped by state, the ACTIVE tab moved to another state band (`_noteActiveTabBand()`, both render paths, scrolling row only; another tab's move never scrolls). ⚠️ The phone-block `min-width` on `.session-tab.active .tab-name` keeps the tab's centre off the gear/close icons, sized for numberless tabs 10+ (floor 40px); do not shrink it. ⚠️ Never reintroduce hoisting the active session to the front of the strip. Tests: `test/mobile-tab-tap-zones.test.ts`, `test/tab-triage.test.ts`. → [architecture-invariants#mobile-tab-strip-scrolling](docs/architecture-invariants.md#mobile-tab-strip-scrolling) -**Session list layout: header strip or left sidebar** (`sessionListLayout`, default `header`; per-device via `displayKeys`, also in `SettingsUpdateSchema`): the list can move into a collapsible `