Skip to content

Reorder sidebar sections by dragging their header - #909

Merged
wingleeio merged 3 commits into
zeronsh:mainfrom
jsgrrchg:zeron/sidebar-section-drag-implementation
Oct 17, 2026
Merged

wingleeio merged 3 commits into
zeronsh:mainfrom
jsgrrchg:zeron/sidebar-section-drag-implementation

Conversation

@jsgrrchg

@jsgrrchg jsgrrchg commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Custom sidebar sections can now be reordered by dragging their header on desktop. The order syncs across devices like pins and section members do. Until now, sections always showed in creation order.

Data and sync

  • SidebarSectionChange::Move { id, after, before } places a section between its neighbours, using the same anchor rule as Assign (section_insert_index). Its project() reorders the local Vec, which covers the optimistic queue and Local workspaces.
  • Each sidebarSections row gets its own orderKey, minted with pin_order_key_between between the neighbours' keys. Concurrent moves of one section converge by clock, and moves of different sections both survive.
  • Ordering: keyed sections by (orderKey, id), then unkeyed ones by (createdAt, id). Until the first move, nothing changes. Sections created later, including by older apps or iOS, append.
  • The first move keys every unkeyed section in its current order. This reuses the member-placement logic, now factored into place_among and shared by members and sections.
  • No edge change: sidebarSections has no field allow-list.

Compatibility

  • New capability sidebar-section-order-v1. An older engine would reject the unknown action, so the UI only offers the drag in Local scope, or in synced scope when the engine advertises it.
  • Apps that predate this ignore orderKey and keep showing creation order. They lose nothing: their renames and collapses don't touch the key.
  • Mobile gets no reorder UI. A rebuilt iOS app picks up the order automatically through zeron-doc.

Desktop UI

  • Dragging a section header lifts the whole section, frosted like a lifted session, while its siblings slide aside (TAB_SLIDE). The section lands past a sibling once its leading edge crosses that sibling's middle, so sections of different heights reorder naturally.
  • The list edge-scrolls during the drag. Releasing outside the sidebar settles the section back home, and drops and cancels glide through the existing resort animation. Reduced motion is respected.
  • Header clicks still collapse and expand.

iOS: drag sessions between sections

  • Long-press a session on the front page and drag it into Pinned or a user section at the slot it's dropped on, or back to Recent. Placements carry after/before anchors, so the order syncs with desktop. A folded destination opens to show the row.
  • Sections now list their members in their placed order, as desktop does, instead of by recency.
  • Client/FFI: pin_session_at and place_session; assign_section places without anchors. zeron_core.swift regenerated.
  • Reordering sections themselves stays desktop-only.
  • Tests: sidebar_writes_reorder_the_front_page (client demo) covers anchored placement, unpinning on placement and anchored pins.

Tests

  • section_move_reorders_sections_by_anchor (proto)
  • sidebar_sections_reorder_and_converge_across_devices (doc): unmoved order, appended unkeyed sections, concurrent moves of different sections and of the same section, deleted sections and anchors
  • sections_reorder_by_dragging_their_header (ui): real mouse drag, plus a release outside the sidebar
  • section_drop_index_follows_the_leading_edge (ui)

docs/registry-sync.md describes section ordering.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Sections now keep a placed order. Each section row carries its own
orderKey, minted between its neighbours' keys as pins are, so the order
syncs across devices and concurrent moves converge. Sections no move has
keyed yet, including those older apps create, follow by creation.

On desktop, dragging a section header lifts the section and slides its
siblings aside; releasing outside the sidebar sends it home. Synced
workspaces need an engine advertising sidebar-section-order-v1.
jsgrrchg added a commit to jsgrrchg/comet that referenced this pull request Oct 15, 2026
jsgrrchg and others added 2 commits October 16, 2026 17:58
On the iOS front page, long-pressing a session and dragging it moves it
into Pinned or a user section at the slot it is dropped on, or back to
Recent. Placements carry their neighbours as after/before anchors, so the
order syncs with desktop. A folded destination opens to show the row.

Sections now list their members in their placed order, as desktop does,
instead of by recency. The client and FFI gain pin_session_at and
place_session; assign_section places without anchors.
Placing a section (or member) re-keyed every row once any lacked a key,
so a move on one device after a section was created could overwrite a
concurrent move from another device with a later clock. Key only the
keyless tail, past the last key, and fall back to re-keying everything
only when no key fits there. Also de-flake the same-section concurrent
move test and dedupe section members in the client view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@wingleeio wingleeio 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.

Security audit: clean — hostile/oversized orderKey values fall back to creation order, anchors are validated, no unwraps on synced data, FFI is plain String passthrough, regenerated Swift matches uniffi output, capability gate is correct.

Pushed one fix (e7ad957): placing a section re-keyed every section once any lacked a key, so after a section was created, the next move could overwrite a concurrent move from another device with a later clock. Now only the keyless tail is keyed (past the last key), with a full re-key only when no key fits. Added a regression case to sidebar_sections_reorder_and_converge_across_devices, de-flaked its same-section concurrent step, and deduped section members in the client view. Thanks!

@wingleeio
wingleeio merged commit 26c8e84 into zeronsh:main Oct 17, 2026
23 checks passed
jsgrrchg added a commit to jsgrrchg/comet that referenced this pull request Oct 17, 2026
Upstream squash-merged zeronsh#895, zeronsh#903 and zeronsh#909, already merged here from
their branches. Conflict resolution:
- Take upstream's final versions of those PRs (session hover card
  reconcile, side chat rename leaving takeover, section reorder tests).
- Keep this branch's cross-PR changes: right_layout/right_panes modules
  (zeronsh#939), chat-unread capability (zeronsh#515), native_fork_point (zeronsh#622), and
  the observe_sidebar_row -> observe_row rename (zeronsh#515), also applied to
  upstream's new sidebar section code.
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 23, 2026
…07 becomes the session-card superset; 12 superseded into 07
hoangvu12 added a commit to hoangvu12/roboco that referenced this pull request Oct 23, 2026
Wave-7 ticket 06 — port of upstream 05f5f2b + ae19fe3 + 672cc14,
with the 2026-10-22 riders 26c8e84 (zeronsh#909, drag-reorder) and
81e04e1 (zeronsh#901, Archived toggle) folded in; the 05f5f2b/ae19fe31
head start came from the saved ticket-06 fold-ins patch.

Upstream 672cc14 (searchable popovers, ordered sidebar sections,
titlebar sidebar options):
- popover: every searchable popover (project, device, branch, spaces,
  changes ref, font) shares the model picker's search row (40px,
  magnifier, full-bleed hairline) and 32px picker rows in one tone with
  scroll edge fades and rails (search_input_frame/search_row rework +
  picker_row/picker_icon_slot/picker_divider).
- Project rows in both project dropdowns wear the project artwork
  (ProjectIcons extracted from Shell's inline cache into the shared
  pipeline shape, now owned by Shell and Pickers); the device tag is
  gone from the spaces dropdown and the sidebar filter trigger.
- All sidebar section headings share one header; collapsed sections
  show a muted count badge (monospace, cut like the PR badge) instead
  of a "(N)" label suffix; header glyphs share the label's tone.
- Custom sections keep a placed order, as Pinned does: assign carries
  optional after/before anchors (section_insert_index, the pin rule),
  drags reorder members in place (sidebar_section_drop_anchors reads
  the preview gap), and the sidebar lists section members in placed
  order rather than by activity.
- The view options moved to the titlebar at the sidebar's trailing
  edge (render_sidebar_options_titlebar, sliding/fading with the
  sidebar, occluded over the drag strip); View > Show gains a Project
  filter toggle (hiding it clears the filter) and SIDEBAR_MIN widened
  224 -> 242 so the options button fits beside the titlebar controls.
- Polish: agent-update pill summary fits the card border (no left
  strip), thinking/tool-call group headers roll as their summary
  changes.

Rider 26c8e84 (zeronsh#909): dragging a section header lifts the section
(frosted, in the list's movement layer) and slides its siblings aside;
releasing outside the sidebar sends it home. Roboco's engine-local
equivalent of SidebarSectionChange::Move: the reorder splices the
sections list and pushes the SetSidebarSections snapshot.

Rider 81e04e1 (zeronsh#901): sidebar toggle for the Archived section
(sidebar_show_archived) — hidden removes the whole shelf without
touching its chats or disclosure state; a reveal into it re-enables it.

Wire change: the SidebarSection proto doc now states the placed-order
contract — membership and the section list carry their placed order in
the array order through the engine-local LWW replaces. Upstream's
per-member fractional orderKey registry rows + HLC clocks exist only
for cross-device convergence, which roboco excludes (ADR 0004); the
engine store already preserves list order, so no per-key minting and
no sidebar-section-order-v1 capability gate were carried. Wiregen
regenerated (SidebarSection.ts doc); --check gate green. The web peer
(sectionRows) lists members in placed order, with tests.

Excluded by intent: crates/client hunks (cloud), pull_requests.rs
popover hunks (rides parked zeronsh#484/ticket 21), crates/mobile FFI
pin_session_at/place_session (Android parity — held for a mobile
follow-up per ticket step 6), iOS hunks, and the registry
cross-device convergence tests (replaced by the engine-side
placed-order restart test plus UI placed-order/drag/anchor tests).

Verification: cargo check -p roboco-doc -p roboco-proto -p roboco-ui;
cargo test -p roboco-ui (1618 passed); sidebar-sections/spaces/
pickers/appearance/changes/chat-rename/transcript/harness-updates
tests; cargo test -p roboco-engine sidebar_state (10 passed);
wiregen --check green; pnpm -r build green; web vitest sidebar suites
green (6 unrelated failures reproduce on clean main).
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