feat(ui): hold header rows and page sections to rearrange or hide them #1

Open
gabrielvidal wants to merge 5 commits from navbar-hold into main
Owner

Summary

Hold-to-rearrange was already in the app, but only for a handful of lists, and nothing could be hidden. This adds the stash — drop an item on the popover to bench it, drag its tag back to restore it — and extends the gesture to the places that were still fixed: every header icon-button row, and the Project / Service / Settings page bodies.

Which places were picked came from a form (ask-form): the bottom navbar tabs and the already-reorderable lists were explicitly left alone; the four new areas below are what was asked for.

Key changes

  • Dropped the local HoldEditable fork. The app carried its own 767-line copy, frozen before the stash existed. @gabvdl/ui's is a strict superset, so the nine call sites now import it and the fork is gone.
  • useListArrangement (lib/listOrder.ts) — useListOrder's other half: which entries are benched. Persisted server-side in a new listStashes bag beside listOrders, reconciled the same way (unknown ids dropped, unknown-to-the-save entries slotted rather than benched).
  • HeaderActions — a header's icon-button row, reorderable and stashable. Wired into the conversation header (interrupt · expand-all · visibility · details), the drawer header, and the page headers on Notifications home, All conversations, Notifications and Goals.
  • SectionStack — a page's stack of sections, same gesture. Wired into Project (10 sections), Service (12) and Settings (4).
  • @gabvdl/ui 0.30.3 — fixes the stash popover for groups taller than the viewport (see below). Published to verdaccio; the design-system commit is pushed to both remotes.

Key decisions

  • A group can never be emptied. It is only reachable through its own members, so an empty one could never be held again to reopen its stash. Both wrappers floor the group at one slotted item, and HeaderActions adds pinned for controls whose absence would strand the user — a back link, the drawer's mobile close, Interrupt on a live session.
  • The upstream fix was the honest fix. The popover hung off the group's raw rect, so on a twelve-section Service page it anchored off screen: the stash existed but you could not see or drop onto it. Rather than work around it per-page, @gabvdl/ui now clips the anchor to the viewport, flips to the other side when the chosen one is against an edge, and pins the popover inside the viewport when the group fills it.
  • The order bag keeps benched items too, appended after the slotted ones, so a tag dragged back lands in its old slot instead of at the end.
  • Sections are filtered inside the stack, not by the caller, so the list reads as the page's full repertoire and a section that comes back later (a GOAL.md added, a first commit) returns to its saved slot.
  • The drawer's close button now renders conditionally instead of via md:hidden: a display:none slot measures zero, which would leave the drag a dead gap to hit.

Changelog

Added

  • Hold any header icon button for a moment to rearrange the row — or drop a button on the stash popover to hide it, and drag its tag back to bring it home. Covers the conversation header, the drawer header, and the Notifications / All conversations / Notifications / Goals page headers.
  • Hold a section on a Project, Service or Settings page to reorder the page or bench sections you never read.
  • Both the order and the hidden set are saved server-side, so a phone and a desktop show the same arrangement.

Changed

  • The stash popover now stays on screen for pages taller than the viewport (@gabvdl/ui 0.30.3).

Removed

  • The app's private copy of HoldEditable, in favour of the design-system component.

Test notes

  • tsc --noEmit and vite build clean; build:mock served and driven with Playwright.
  • Drove the real gesture, not just the render: held a header button (edit mode + popover open, screenshot), held a settings card, scrolled a settings page past the stack's top edge and dropped a card onto the popover — it benched, the card left the page, its tag appeared, and the popover pinned itself inside the viewport (the 0.30.3 fix).
  • Project, Service, Settings and Projects pages screenshotted before/after: layout unchanged.
  • Not exercised: cross-device persistence against the live backend (mock API only).

Screenshots

02-settings-sections-edit.png
03d-stashed.png
05-conversation-edit.png

## Summary Hold-to-rearrange was already in the app, but only for a handful of lists, and nothing could be **hidden**. This adds the stash — drop an item on the popover to bench it, drag its tag back to restore it — and extends the gesture to the places that were still fixed: every header icon-button row, and the Project / Service / Settings page bodies. Which places were picked came from a form (`ask-form`): the bottom navbar tabs and the already-reorderable lists were explicitly left alone; the four **new** areas below are what was asked for. ## Key changes - **Dropped the local `HoldEditable` fork.** The app carried its own 767-line copy, frozen before the stash existed. `@gabvdl/ui`'s is a strict superset, so the nine call sites now import it and the fork is gone. - **`useListArrangement`** (`lib/listOrder.ts`) — `useListOrder`'s other half: which entries are benched. Persisted server-side in a new `listStashes` bag beside `listOrders`, reconciled the same way (unknown ids dropped, unknown-to-the-save entries slotted rather than benched). - **`HeaderActions`** — a header's icon-button row, reorderable and stashable. Wired into the conversation header (interrupt · expand-all · visibility · details), the drawer header, and the page headers on Notifications home, All conversations, Notifications and Goals. - **`SectionStack`** — a page's stack of sections, same gesture. Wired into Project (10 sections), Service (12) and Settings (4). - **`@gabvdl/ui` 0.30.3** — fixes the stash popover for groups taller than the viewport (see below). Published to verdaccio; the design-system commit is pushed to both remotes. ## Key decisions - **A group can never be emptied.** It is only reachable through its own members, so an empty one could never be held again to reopen its stash. Both wrappers floor the group at one slotted item, and `HeaderActions` adds `pinned` for controls whose absence would strand the user — a back link, the drawer's mobile close, `Interrupt` on a live session. - **The upstream fix was the honest fix.** The popover hung off the group's raw rect, so on a twelve-section Service page it anchored off screen: the stash existed but you could not see or drop onto it. Rather than work around it per-page, `@gabvdl/ui` now clips the anchor to the viewport, flips to the other side when the chosen one is against an edge, and pins the popover inside the viewport when the group fills it. - **The order bag keeps benched items too**, appended after the slotted ones, so a tag dragged back lands in its old slot instead of at the end. - **Sections are filtered inside the stack**, not by the caller, so the list reads as the page's full repertoire and a section that comes back later (a GOAL.md added, a first commit) returns to its saved slot. - The drawer's close button now renders conditionally instead of via `md:hidden`: a `display:none` slot measures zero, which would leave the drag a dead gap to hit. ## Changelog ### Added - Hold any header icon button for a moment to rearrange the row — or drop a button on the stash popover to hide it, and drag its tag back to bring it home. Covers the conversation header, the drawer header, and the Notifications / All conversations / Notifications / Goals page headers. - Hold a section on a Project, Service or Settings page to reorder the page or bench sections you never read. - Both the order and the hidden set are saved server-side, so a phone and a desktop show the same arrangement. ### Changed - The stash popover now stays on screen for pages taller than the viewport (`@gabvdl/ui` 0.30.3). ### Removed - The app's private copy of `HoldEditable`, in favour of the design-system component. ## Test notes - `tsc --noEmit` and `vite build` clean; `build:mock` served and driven with Playwright. - Drove the real gesture, not just the render: held a header button (edit mode + popover open, screenshot), held a settings card, scrolled a settings page past the stack's top edge and dropped a card onto the popover — it benched, the card left the page, its tag appeared, and the popover pinned itself inside the viewport (the 0.30.3 fix). - Project, Service, Settings and Projects pages screenshotted before/after: layout unchanged. - Not exercised: cross-device persistence against the live backend (mock API only). ## Screenshots ![02-settings-sections-edit.png](https://git.gabvdl.xyz/attachments/dd222702-9986-4dec-bbec-cadde44c6cad) ![03d-stashed.png](https://git.gabvdl.xyz/attachments/2fd5bf94-6cc5-429b-b107-78bfb2558e10) ![05-conversation-edit.png](https://git.gabvdl.xyz/attachments/8b10f04d-56b0-443b-9c92-d5bcae6b7741)
gabrielvidal self-assigned this 2026-08-09 17:24:16 +02:00
gabrielvidal added 5 commits 2026-08-09 17:24:17 +02:00
The app shipped its own copy of HoldEditable, frozen at the version that
predates the stash. @gabvdl/ui's is a strict superset — stash, grid reflow,
two-stage holds, per-item itemClassName — so keeping the fork is 767 lines
of drift waiting to happen. Point the call sites at the package (0.30.3,
which also keeps the stash popover on screen for tall groups) and delete it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
useListOrder makes a hard-coded list rearrangeable; useListArrangement adds
the other half — which of its entries are benched out of the group entirely
— and hands both straight to HoldEditable. Stashed ids live in their own
`listStashes` bag next to `listOrders`, reconciled the same way: unknown ids
are dropped, and an entry the saved state never heard of is slotted rather
than benched, so shipping a new one always shows it. The order bag keeps
benched items too, so a tag dragged back lands in its old slot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two thin wrappers over HoldEditable + useListArrangement, one per shape the
app repeats: a header's icon-button row, and a page's stack of sections.
Both floor the group at one slotted item — a group is only reachable through
its own members, so an empty one could never be held again to reopen its
stash — and HeaderActions adds `pinned` for the controls whose absence would
strand the user (a back link, a mobile close, Interrupt on a live session).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every header action row goes through HeaderActions: the conversation header
(interrupt, expand-all, visibility, details), the drawer header, and the
page headers on Notifications home, All conversations, Notifications and
Goals. Hold one for a moment and the row starts jumping; drop a button on
the stash popover to bench it, drag its tag back to bring it home. Goals'
running-agents badge rides in the row so it can be benched too.

The drawer's close button now renders conditionally instead of via
`md:hidden` — a display:none slot measures zero, which would leave the drag
a dead gap to hit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Project, Service and Settings render their bodies through SectionStack, so
the page is arrangeable the same way its header row is: hold a card, drop it
on the stash to bench it. A Service page is twelve sections deep and most
visits care about two of them; Settings is mostly write-once. Sections a
page cannot render (no GOAL.md, no commits) are filtered inside the stack,
so a section that comes back later returns to its saved slot.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This pull request has changes conflicting with the target branch.
  • frontend/package-lock.json
  • frontend/package.json
  • frontend/src/business/composer/components/BottomBar.tsx
  • frontend/src/business/conversations/pages/Conversation.tsx
  • frontend/src/business/dashboards/components/DashboardsPanel.tsx
  • frontend/src/business/dashboards/pages/ConversationsAnalytics.tsx
  • frontend/src/business/dashboards/pages/Dashboard.tsx
  • frontend/src/business/files/components/FilesPanel.tsx
  • frontend/src/business/files/pages/FilesHome.tsx
  • frontend/src/business/projects/pages/Project.tsx
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin navbar-hold:navbar-hold
git checkout navbar-hold
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gabrielvidal/ai-agent#1