Skip to content

feat(chat): drag-to-reorder pinned threads + Move up/Move down#280

Merged
blove merged 10 commits into
mainfrom
claude/chat-pinned-reorder-impl
May 13, 2026
Merged

feat(chat): drag-to-reorder pinned threads + Move up/Move down#280
blove merged 10 commits into
mainfrom
claude/chat-pinned-reorder-impl

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented May 13, 2026

Summary

Adds drag-to-reorder for pinned threads in @ngaf/chat. Desktop: hover-revealed grip handle on the left of pinned rows initiates native HTML5 drag-and-drop with a drop indicator line. Mobile (and as a discoverable fallback on desktop): "Move up" / "Move down" entries appear in the row's existing overflow menu. Both code paths converge on ThreadActionAdapter.reorderPinned?(threadId, beforeId | null) — framework owns optimistic visual reorder and rollback on rejection.

Spec: docs/superpowers/specs/2026-05-12-chat-pinned-reorder-design.md
Plan: docs/superpowers/plans/2026-05-12-chat-pinned-reorder.md

New adapter method

export interface ThreadActionAdapter {
  // ... existing
  /** Reorder a pinned thread. `beforeId` = id to place before, or null for end. */
  reorderPinned?(threadId: string, beforeId: string | null): Promise<void>;
}

Extended

  • chat-thread-list: new pendingOrder override signal, draggingThreadId + dropTarget transient state; updated visibleThreads applies pending order; new performReorderPinned / performMoveUp / performMoveDown handlers; HTML5 drag handlers (onDragStart / onDragOver / onDragLeave / onDrop / onDragEnd); grip-handle template + drag attributes on each row; "Move up" / "Move down" menu items conditional on pinned position; updated showKebab to include reorderPinned.
  • Styles: grip handle with hover-reveal opacity + grab/grabbing cursors; drop indicator via ::before / ::after on [data-drop-position]; row dimming during drag.
  • Example ThreadsService: reorderPinned PATCHes metadata.pinnedOrder on each affected pinned thread (re-stamps 0..N-1); toThread reads metadata.pinnedOrder; refresh sorts pinned-first then by pinnedOrder.

Test plan

  • nx run chat:test — existing + 12 new reorder tests pass
  • nx run chat:build && nx lint chat — clean
  • nx run examples-chat-angular:build — clean
  • API docs regenerated
  • Browser-verified via Chrome MCP: pinned two threads → grip handles render → first-pinned menu has Move down (no Move up); last-pinned menu has Move up (no Move down); Move up on second swaps the two rows; LangGraph metadata persisted.

Scope

  • Pinned threads only — unpinned rows are unaffected.
  • Anchor-based event (beforeId | null) — stable across concurrent mutation.
  • No @angular/cdk dependency (native HTML5 drag-and-drop).
  • Mobile uses Move up/Move down menu items (no touch-drag gesture).

Out of scope (deferred)

  • Long-press touch-drag on mobile (replaced by menu items).
  • Drag-handle keyboard reorder shortcut (menu items cover keyboard users).
  • Drop indicator auto-flipping when near viewport bottom.

🤖 Generated with Claude Code

@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

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

Project Deployment Actions Updated (UTC)
cacheplane Ready Ready Preview, Comment May 13, 2026 2:39am

Request Review

blove and others added 9 commits May 12, 2026 19:26
Drag-to-reorder via grip handle on desktop; "Move up" / "Move down"
menu items on mobile (and as discoverable fallback on desktop).
Anchor-based adapter event: reorderPinned(threadId, beforeId | null).
Framework owns optimistic visual reorder + rollback. Scoped to pinned
threads only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 tasks: ThreadActionAdapter.reorderPinned, pendingOrder state +
visibleThreads override, drag handlers, Move-up/Move-down menu items,
grip handle template + drag attributes, styles, tests, example
ThreadsService.reorderPinned (LangGraph metadata.pinnedOrder),
demo-shell threadAction, API docs regen, browser verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add reorderPinned? to ThreadActionAdapter, pendingOrder/draggingThreadId/dropTarget
signals, update visibleThreads with optimistic pinned-order overrides, wire
move-up/move-down menu items, and add all drag-and-drop handler methods.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jsdom doesn't run layout, so wrap elements returned a zero-height rect
which inverted the before/after threshold and made both drop tests
assert the wrong anchor id. Stub the rects to 40px each and use
absolute clientY values so the position math matches a real layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@blove blove merged commit b2eaf29 into main May 13, 2026
14 checks passed
blove added a commit that referenced this pull request May 13, 2026
…ft) (#291)

* docs: spec for grip-replaces-pin-on-hover

Eliminate the always-reserved 18px left gutter that pinned rows take
for the grip button. Swap the drag affordance into the same slot as
the pin icon via opacity transition. Zero layout shift, zero new
state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: plan for grip-replaces-pin-on-hover

Single-task TDD plan: structural test → template restructure → styles
replacement → verify all PR #280 tests still pass → PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(chat): drag handle replaces pin icon on hover (no left-gutter shift)

PR #280 introduced a standalone grip button as a sibling of the row's
main click button. The grip occupies 18px (16px width + 2px margin)
of inline space whether visible or not, so pinned rows sat 18px right
of unpinned rows. The user disliked the constant layout shift.

Drop the standalone grip. Wrap the existing pin SVG in a fixed-size
.chat-thread-list__pin-slot span; render the grip glyph as an
absolutely-positioned sibling inside that slot. CSS opacity-toggle on
:hover / :focus-within swaps which icon is visible. Zero layout
shift, same affordance.

The <li> remains the drag source — all drag handlers untouched. Move
up / Move down menu items (keyboard path) unchanged.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant