Skip to content

feat(web): complete live roster drag for bots and groups - #218

Open
leoisadev1 wants to merge 3 commits into
mainfrom
leoisadev1/t3-port-roster-drag
Open

feat(web): complete live roster drag for bots and groups#218
leoisadev1 wants to merge 3 commits into
mainfrom
leoisadev1/t3-port-roster-drag

Conversation

@leoisadev1

@leoisadev1 leoisadev1 commented Sep 10, 2026

Copy link
Copy Markdown
Member

Problem

The live Akeru roster (BotRosterSidebar) used @hello-pangea/dnd. Pins were a display-only strip, not a sortable destination. Drag cleanup did not cover Escape, blur, hidden tab, unmount, or a missed release. Insertion-gap motion, destination labels, and empty/collapsed targets from upstream thread-sidebar drag never reached the live bot/group UI.

Change

Adapt upstream sidebar drag onto the live roster, not the dead Sidebar.tsx.

  • One vertical sortable list of pinned bots/groups, named sections, and Unassigned
  • Pin and unpin by drag, with Pin / Unpin / Move labels on the lifted row
  • Insertion-gap continuity, readable destination labels, empty/collapsed/last-item-leaves targets
  • Pointer cleanup on release, Escape, blur, hidden tab, resize, unmount, and missed release
  • Click suppression so a completed drag does not navigate; ordinary clicks still open the bot
  • Keyboard reorder through Move up / Move down on the existing row and section action menus, so Enter/Space on a live bot or group row still opens it
  • Cancelled-drag click suppression ignores keyboard-generated clicks (detail === 0) and clears on the next keydown
  • List motion that glides from the released positions and respects reduced motion
  • Roster layout stays separate from group membership and never settles chats

Layout is still browser-local per environment. Server-backed roster order, sidebar file drops, and native arrange are follow-up PRs.

Upstream

Reviewed adaptations of pingdotgg/t3code:

Scope checklist

Port This PR Later
pingdotgg#9731 pingdotgg#9750 pingdotgg#10378 pingdotgg#10453 pingdotgg#10464 live roster drag yes
pingdotgg#9729 roster persistence (not thread activeOrder) next
pingdotgg#7892 sidebar file-drop onto bot/group chats later
pingdotgg#9730 pingdotgg#10496 native arrange later

Verification

  • vp test run on roster logic/store/drag/pointer/motion plus SidebarChrome and shortcut guards: 96 passed
  • vp lint and vp run --filter @t3tools/web typecheck on the changed files: clean
  • Isolated vp run dev --home-dir /tmp/akeru-roster-drag.VVPXZ4 with pairing
  • Desktop: created Mori and Akeru, dragged Akeru above Scout, dragged Mori into Pinned, dragged it back to Unassigned, Escape cancelled an in-progress drag, click still opened Mori, menu Pin survived reload
  • Group Crew: dragged the group row to the top of the roster (now first, above Mori). Chat stayed on Mori (/bots/...); the drag did not navigate.
  • Mobile 390×844: opened Toggle main sidebar, then dragged Akeru above Scout inside the sheet. Order became Crew, Akeru, Mori, Scout.
  • Settings overlay still opens
  • Keyboard (head ceb469ba2): Actions menu Move up on Mori (Crew, Mori, Scout, Akeru), Move down on Crew (Mori, Crew, Scout, Akeru). Enter on the Akeru row opened Akeru chat; Enter on Crew opened the group chat (no KeyboardSensor drag). Pointer-drag Scout, Escape cancel, then Enter on Akeru still opened Akeru. Blur cancel, then Actions Move down on Mori (Crew, Mori, Scout, Akeru). Hidden/visibility cancel without pointerup, then Enter on Scout still opened Scout.

Keyboard Move up: Mori above Scout
Keyboard Move down: Crew under Mori
Escape cancel left order unchanged
Enter after Escape cancel opened Akeru
Enter after hidden cancel opened Scout

GroupThreadLanding hook crash (baseline)

Opening /groups/... still hits Rendered more hooks than during the previous render in GroupThreadLanding at useReplyPlaybackThread (line 96), after if (!group) return null (line 90).

Isolated baseline proof, not “the file was untouched”:

Before: one bot under Unassigned
Three bots ready to drag
After reorder: Akeru above Scout
After pin: Mori above Unassigned
After reload: pin and order kept
Mobile viewport

roster-drag.webm

Limitations

  • Order is stored in the browser for the connected environment. Other devices do not share it until the persistence PR.
  • Named-section creation still uses window.prompt.
  • Native arrange and sidebar file-drop are not in this PR.

Grok 4.6 High in Grok Build via Orca.

Before dragging Crew

After dragging Crew to the top

Mobile sidebar open before drag

Mobile sidebar after dragging Akeru

The live BotRosterSidebar could reorder sections and rows with hello-pangea, but pins were not a drop target and drag cleanup was incomplete. Port the upstream sidebar pointer, insertion-gap, and motion behavior onto the live bot/group roster without settling chats or changing group membership.
@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
akeru-bot-landing Building Building Preview Sep 10, 2026 5:44pm UTC

Request Review

@github-actions github-actions Bot added area:directory Plugin directory and its user interface. vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Sep 10, 2026
@leoisadev1

Copy link
Copy Markdown
Member Author

This is Leo's agent.

GroupThreadLanding hook-order crash, reproduced on baseline rather than inferred from an untouched file:

Follow-up drag evidence on this branch: Crew group row dragged to the top without navigating away from Mori; mobile sidebar opened and Akeru dragged above Scout in the sheet.

@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown

Greptile Summary

The roster can show Move up/down controls for newly live unassigned bots or groups that are absent from an existing saved order, but those controls do not update the displayed or saved order.

Confidence Score: 4/5

Not safe to merge until newly live unassigned roster entries can be reordered by their enabled controls.

The reproduced roster failure prevents enabled Move up/down actions from working for newly live unassigned entries omitted from a saved order. The keyboard-reordering thread was manually resolved by greptile-apps[bot] without explanation. The cancelled-drag click-suppression thread was manually resolved by greptile-apps[bot] without explanation. The keyboard-action drag thread was manually resolved by greptile-apps[bot] without explanation.

Files Needing Attention: apps/web/src/components/roster/rosterStore.ts

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a finding-comment-proof for a posted P1 finding, with validation anchored to the review comment.
  • T-Rex produced a second finding-comment-proof for a second posted P1 finding, with validation anchored to its review comment.
  • T-Rex completed a general-contract-validation-proof, including an executable reproduction and notes on roster state, order, and rendering behavior.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Enabled Move up does not reorder newly live unassigned items absent from persisted order

    • Bug
      • When a saved nonempty unassignedItems order omits a newly live, unpinned, unsectioned bot or group, the sidebar appends it visually and enables Move up when it is not first. Activating that enabled item action passes the item to nudgeRosterItem, but the store builds its source order from only the persisted list, which omits the item. The move returns no result, so the displayed order and persisted state remain unchanged.
    • Cause
      • rosterStore.ts:541-548 chooses state.unassignedItems.filter(remaining) whenever the persisted array is nonempty, unlike the rendering logic that appends missing live items. Consequently moveRosterItemInOrder at line 549 cannot find the newly live item, and line 550 returns before set/saveState.
    • Fix
      • Build the unassigned order used by nudgeRosterItem with the same reconciliation as the sidebar: retain valid persisted items, then append all remaining live unpinned/unsectioned groups and nonarchived bots not already included before calling moveRosterItemInOrder. Persist that reconciled moved order.

    T-Rex Ran code and verified through T-Rex

Fix all with Greploop Fix All in Claude Code

Reviews (3): Last reviewed commit: "fix(web): keep roster keyboard reorder o..." | Re-trigger Greptile

Comment thread apps/web/src/components/roster/BotRosterSidebar.tsx
Comment thread apps/web/src/components/roster/roster.pointer.ts
Greptile found that the live roster registered only a pointer sensor, and that a cancelled drag could leave a capture-phase click suppressor that blocked the next keyboard activation. Register dnd-kit's keyboard sensor and drop that suppressor on keydown.
Comment thread apps/web/src/components/roster/BotRosterSidebar.tsx Outdated
dnd-kit KeyboardSensor made Enter/Space start a drag on live bot and group rows. Put Move up/down on the existing action menus, and ignore keyboard-generated clicks plus the next keydown in cancelled-drag click suppression.

Grok 4.6 in Akeru Bot.
Comment on lines +540 to +550
const unassigned =
state.unassignedItems.length > 0
? state.unassignedItems.filter(remaining)
: [
...state.groups.map((group) => ({ kind: "group" as const, id: group.id })),
...state.bots
.filter((bot) => bot.archivedAt === null)
.map((bot) => ({ kind: "bot" as const, id: bot.id })),
].filter(remaining);
const movedUnassigned = moveRosterItemInOrder(unassigned, item, delta);
if (!movedUnassigned) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Unassigned moves silently fail

When a saved nonempty unassigned order does not include a newly live, unpinned, unsectioned bot or group, the sidebar appends that item visually and enables its Move up control. This code instead builds the reorder source from only the saved list, so it cannot find the new item and returns without updating or saving anything. The enabled control silently does nothing, leaving users unable to reorder newly discovered unassigned entries.

Artifacts

Evidence from the check

  • This authored TypeScript script initializes the persisted and newly live unassigned roster scenario, invokes the real store action, and records the resulting state; it directly exercises the affected behavior.

Command output from the check

  • The completed command output shows Move up enabled for Bravo while persisted order and persistence writes remain unchanged after invoking the real nudge action; the supplied issue is confirmed.

Command output from the check

  • This command output confirms the temporary source-tree browser harness was removed before reporting; no temporary source harness remains.

View artifacts

T-Rex Ran code and verified through T-Rex

Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:directory Plugin directory and its user interface. size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant