Let the reader put the session list in its own order

Nothing sorts the sessions tab any more. The order is the server's
`sessions` list, which is the reader's arrangement: holding a row puts the
screen in selection mode -- the same gesture and the same bottom bar as the
import tab -- and each card grows a burger handle at its right edge that
drags the row to a new place, with a tick of haptic feedback for each one it
passes.

The two attempts this replaces, sorting by activity and then by when each
agent was turned on, were both looking for an order a session could not move
itself out of; no rule computed from what a session is doing can be one.
`POST /sessions/order` rewrites the config's order, so it is the same on
every device and survives a backend restart, and `SessionConfig::started`
goes with the sort that needed it.

Rearranging is independent of the selection: the handle moves the row it is
on, picked out or not. The click moved off the card and onto its contents so
that a press landing on the handle cannot also select the row it is about to
move. Selection's one action is Delete, which now takes the whole set.

Two traps in `Reorder.kt`, both measured on the emulator and written down in
`this-machine-android`: a crossing is decided from how far the finger has
travelled, because a lazy list animates an item into its new place and its
`offset` reports the old one for several frames; and the viewport is pinned
with `requestScrollToItem` around each move, because a lazy list keeps its
place by the key of the top item and would otherwise follow the row being
dragged.

Verified on the emulator against the sandbox: the order survives an app
restart and a backend read-back, a two-row drag moves exactly two rows, a
drag to the bottom edge scrolls the list and lands the row last, pressing the
handle without moving changes nothing, and deleting two selected sessions
leaves the rest in place.
This commit is contained in:
iris-ai committed 2026-09-20 01:06:15 -04:00
1 parent 942edd6b31
commit b7fd18b195
12 files changed
+801 -339

No files matched your search

+28 -16
View File
@@ -1428,22 +1428,34 @@ Kotlin + Compose Multiplatform, single `:androidApp` module, same versions as
dev-updater (Kotlin 2.4.x, CMP 1.11.x, JDK 21).
1. **Session list** — kind icon, title, machine, model, status, last activity.
**A session with a process stays where it is, and the order is when each
agent was turned on** (2026-09-15, replacing the awaiting-answer inbox
sort): the running sessions come first, oldest start first, so one that is
started joins the bottom of that group and nothing it goes on to do —
beginning a turn, finishing one, asking a question — can move it. A list
that reorders itself is one nobody can keep their place in, and the status
word and its colour already say which session wants an answer without the
row having to move to say it. Stopped sessions are a group below, most
recently active first; "turned on" is what the order above is made of and a
session with no process has no place in it.
The order is the server's (`SessionConfig::started`, reported as
`started`), written each time a process is started for the session, so it
is the same on every device and survives a backend restart — which adopts
processes rather than starting them, and so could not work the times out
for itself. Applied on the phone (`sessionsInListOrder`), because
presentation order is a display decision.
**Nothing sorts it: the order is the reader's own** (2026-09-20,
superseding first the awaiting-answer inbox sort and then ordering by when
each agent was turned on). Both of those were attempts to find an order a
session could not move itself out of, and the answer is that no rule
computed from what a session is doing can be onethe reader is the only
thing that knows which conversation matters. A list that rearranges itself
is one nobody can keep their place in, and the status word and its colour
already say which session wants an answer without the row having to move to
say it.
The order is the server's `sessions` list, rewritten by
`POST /sessions/order`, so it is the same on every device and survives a
backend restart, and a spawn appends rather than displacing anything.
`SessionConfig::started` and the `started` field went with the sort that
needed them.
**Rearranging is a drag on a handle, in selection mode** — hold a row to
enter it, the same gesture and the same bottom bar as the import tab, and
each card grows a burger handle at its right edge. The handle rather than
the row, because a row that can be dragged is a row that sometimes eats a
fling; and the click is on the card's contents rather than on the card, so
a press that lands on the handle cannot also select the row it is about to
move. The move is applied on the phone as the finger crosses each row, with
a tick of haptic feedback for each, and the whole order is sent when the
finger comes up: a server asked per crossing would be answering about an
arrangement that had already changed. A refused save puts the server's own
order back rather than leaving the screen showing one nothing kept.
Rearranging is deliberately independent of what is selected — the handle
moves the row it is on — because "which rows am I acting on" and "where
does this one go" are two questions.
2. **Import** — Claude Code sessions the machine already has, selected in
batches (hold to enter, tap to add), with Delete and Import along the
bottom. Submitting clears the selection immediately and marks every chosen