Skip to content

feat(tabs): arrange the tab strip by work folder or by status - #835

Open
Jonathan-Asher wants to merge 2 commits into
spacering-net:mainfrom
Jonathan-Asher:pr/tab-arrange
Open

Jonathan-Asher wants to merge 2 commits into
spacering-net:mainfrom
Jonathan-Asher:pr/tab-arrange

Conversation

@Jonathan-Asher

Copy link
Copy Markdown
Contributor

Summary

Adds an "Arrange tabs" menu to the conversation tab strip with three layouts, remembered per device:

  • Manual order — drag to reorder (today's behavior, still the default).
  • Group by work folder — each folder's tabs together behind a chip in the folder's color, with a thin top border in that color on each tab. Folders without a color get a stable automatic color.
  • Sort by status — sessions waiting for you first (permission request, question, or plan awaiting approval), then awaiting your reply, then running, then the rest.

Behavior

  • Display-only: the manual order (rawTabs, persisted via save_opened_tabs) is never rewritten, so switching back to Manual restores exactly the dragged order. Dragging (in-strip and cross-group) is off while a derived layout is shown; the trigger is tinted then.
  • Folder and status runs keep the manual order inside them; empty status bands are skipped.
  • "Waiting for you" is derived on the client from each tab's live ACP connection (pendingPermission, pendingQuestion, pendingAskQuestion, pendingPlanApproval) through a new useTabAttention hook — a tab enters the band as soon as a prompt appears and leaves it once answered. Subscriptions exist only while Sort by status is active, and the map keeps its identity across streaming updates, so a busy session doesn't re-render the strip per token.
  • "Awaiting your reply" / "Running" follow the conversation status the tabs already show (pending_review / in_progress), colored like the status dots.
  • The choice is shared by every strip, split groups included.
  • New strings under Folder.tabs in all 10 locales.

Verification

  • src/lib/tab-arrangement.test.ts — grouping, status bands, attention derivation, folder colors.
  • src/hooks/use-tab-attention.test.tsx — maps blocked tabs, follows a prompt arriving and being answered, no re-render on streaming-only changes, unsubscribes on unmount.
  • Lint, the vitest suite and the static export build run in CI.

An "Arrange tabs" button beside the new-conversation button offers three
layouts, remembered per device:

- Manual order: drag to reorder, exactly as before.
- Group by work folder: each folder's tabs sit together behind a chip in
  the folder's color, and each tab carries a thin top border in that
  color. Folders without a color get a stable automatic one, so every
  group can be told apart.
- Sort by status: sessions waiting for you (a permission request, a
  question, or a plan awaiting approval) come first, then those awaiting
  your reply, then running ones, then the rest. Each band is labeled and
  colored like the tabs' status dots.

"Waiting for you" is read on the client from each tab's live ACP
connection (its pending permission, question, ask-question and
plan-approval state), so a tab moves into that band as soon as a prompt
appears and leaves it once the prompt is answered. The other bands
follow the conversation status the tabs already show.

The arrangement is display-only: the manual order and its persistence
are untouched and come back as they were. Dragging is off while a
derived layout is shown, and every strip, split groups included,
follows the same choice.

@dawNotPoi dawNotPoi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one active-tab visibility regression in the new arrangements.

)
const attentionByTabId = useTabAttention(attentionTabIds)
const arranged = useMemo(
() => arrangeTabs(groupTabs, arrangeMode, attentionByTabId),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the strip overflows, changing Manual → Group by work folder can move the selected tab outside the visible area while its ID stays the same. The existing scrollIntoView effect below depends only on displayActiveId, so it does not run for this new order (the same happens when an active tab changes status/attention). The active tab then appears to disappear until another tab is selected. Please also react to changes in the active tab's displayed position / arrangement; a regression test can switch modes with a fixed active ID and assert that it remains visible.

The strip scrolled the active tab into view only when the active id
changed. A derived layout moves the selected tab without changing its
id: switching Manual to "Group by work folder" or "Sort by status", or
the active tab changing status band (a prompt appearing or being
answered). On an overflowing strip it could then land outside the
visible area and look gone until another tab was selected.

The reveal now also runs when the arrangement mode changes and, outside
manual order, when the active tab's displayed slot changes. Manual
order still keys on the id only: there the active tab moves only under
the user's own drag, and the strip should not scroll mid-gesture.

It runs as a layout effect, so it measures the new layout before the
tabs' layout animation shifts moved tabs back toward their old spots
for the first frame.

tab-bar.test.tsx switches modes with a fixed active id, moves the active
tab across status bands, and checks that neither another tab's band
change nor a manual reorder scrolls the strip.
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.

2 participants