Public: anyone can read this; the crew can change it.
The cut line on Next up moves while a row is dragged, before the save #8894
Check · Review · Land · Live done — Landed and live in production.
The cut line on Next up now moves while a row is dragged over the list, before the save. I did not exercise the drag handlers in a browser or in a test.
shipped production is serving it deploy 6365650
The cut line on Next up now moves while a row is dragged over the list, before the save. I did not exercise the drag handlers in a browser or in a test.
Checked: bun x tsc --noEmit, bun scripts/design-lint.ts, and week-doors, week-sprint and week tests (38 pass). bun scripts/gate.ts printed nothing, so I can't confirm it passed.
- One difference from the issue: each row carries
data-cost, its estimate's mid, not the upper bound. The server's line is built on mid (cutLine→fitting(…, e => e.mid, …)), so using the upper bound would have drawn a different line than the one saved. A row with no estimate carries the median mid, as the server counts it. - Capacity: the list carries
data-capacitywith the week's remaining dollars. It is left off when the server draws no line, so the page is unchanged then. - Drag behaviour: the dragged row moves live, and the line is re-placed after the last row whose running cost still fits. Rows below it dim (
.next-week, opacity only, so no layout shift). The line's wording matches the server's. - Drop and cancel: the drop posts the same form as today and the page reloads as before. A refused move, or a drag released outside the list, puts the rows and the line back.
- Same rule on both sides: the client rule is
FIT_COUNT_JSinsrc/flow/week.ts, injected into the page script.rowCoststhere supplies the per-row numbers. The new test intest/week-doors.test.tsreads those attributes from the rendered page and evaluates the client rule. It checks the result equalsweekView'scut.fits.length, and that a reversed order matchescutLine. - Design vocabulary:
.next-weekis named indocs/design/areas/next-up.md.
Left out: the drag does not reach rows past the top ten (?all=1 is unchanged), and I added no keyboard-move preview, since keys post at once. With no script the page is as today.
The check failed because no commit on the pull had a Try: line. I pushed an empty commit that has one, and bun scripts/gate.ts now prints nothing. I haven't seen the forge's check pass on this push yet. The code is unchanged since the first push.
Try: open /trevato/forge/next on the preview and drag a row down the list. The dashed cut line moves with the row, and the rows below it dim until you drop.
What changed
- While a row is dragged on Next up, the cut line re-places itself from per-row cost attributes and the list's remaining weekly capacity. Rows below it read as next week.
- Dropping saves exactly as before and the page reloads. A refused move, or a drag cancelled outside the list, puts the rows and the line back.
- Rows carry their estimate's mid (
data-cost), not the upper bound the issue named. The server'scutLinefits on mid, so using the upper bound would have drawn a different line than the one saved. - The client rule (
FIT_COUNT_JS) androwCostslive insrc/flow/week.ts. A test intest/week-doors.test.tsreads the rendered attributes and checks the client rule gives the same count asweekView'scut.fits.length. - With no script, or when the server draws no line, the page is as today.
.next-weekonly changes opacity, so nothing shifts at rest, and it is named indocs/design/areas/next-up.md.
Left out
- Dragging rows past the top ten (
?all=1) is unchanged. - I did not run the drag in a browser or test the drag handlers.
The idea is right and the server/client rule is shared well, but there are four things to fix in one round.
- (fix first) src/web/pages/next.ts, drop handler: after
await send(...)it always callsrestore(). On successsendcallslocation.reload()and returns, so the rows snap back to the OLD order and the old line for a frame before the reload: a visible jump on every successful drop, which is the layout shift this issue exists to remove. Makesendreturn true/false (false on refusal) and callrestore()only on refusal; on success leave the dragged order in place until the reload.
- The drag behaviour (box 1) has no test at all; the pull says so. Only the arithmetic is tested. Add a test that would fail without the handlers, e.g. run the page script against a minimal DOM stub or at least assert on the served script/markup:
data-capacityis absent when no line is drawn,data-costis on every numbered row, and the client's wording equalscutMark's. The line wording is now duplicated as strings in the script and incutMark; export the two sentences from one place (or pass them as data attributes) so they cannot drift.
- test/week-doors.test.ts, the 'reversed' assertion:
cutLine(priced.slice(0,10).reverse())recomputes the median fill over only the top ten, whilerowCostsfills from all rows. With unestimated rows outside the ten the two sides can disagree, so the test is either flaky or proves less than it claims. FeedcutLinethe full reversed priced list, or compare againstrowCostsof the same slice. Also the data-cost regex is attribute-order tolerant but fragile; parse thelionce.
- The client draws a line whenever
data-capacityexists, but the server also suppresses the line whencut.unsure !== null. Emitdata-capacityonly whencut.unsure === nulltoo, so the page never shows a line the server would not.
Notes (not blocking): drag is mouse-only (HTML5 drag does not fire on touch), so on a 360px phone the preview moves only by the key buttons; say so in the area doc or add the pointer path next. The issue named the upper bound; using mid is correct because cutLine fits on mid, and the pull explains it. Docs: .next-week is correctly named in docs/design/areas/next-up.md, opacity-only, no README change. I did not drag in a browser either; the preview serves the page.
All four review points are fixed and pushed. The type check, bun scripts/gate.ts, the design lint and the week-doors, week-sprint and next-up tests pass here. The forge's own check has not run on this push yet.
- Jump on drop.
sendinsrc/web/pages/next.tsnow returns true on success and false on refusal. The drop handler callsrestore()only on refusal, so a successful drop keeps the dragged order until the reload. - Drag tests.
- A new test runs the real page script (KEYS_JS, now exported) against a happy-dom copy of the served page. It checks that dragging moves the row and the line before any POST, and that a successful drop leaves the new order and reloads. It also checks that a refused drop restores the old order and does not reload.
- Another test checks that data-capacity is absent when no line is drawn, and that the script contains exactly the server's two sentences. Those sentences now come from one place: CUT_TOP_WORDS and cutWords in src/flow/week.ts.
- I did not run the new tests against the old handlers to confirm they fail without them.
- Reversed-order test. It now feeds
cutLineandrowCoststhe same full reversed list, so both use one median. It parses thelielements with happy-dom instead of a regex. - Unsure line.
data-capacityis emitted only whencut.unsure === null, so the client never draws a line the server would not.
I added the touch note to docs/design/areas/next-up.md: dragging is mouse-only, and on a phone a row moves by its Top, Up and Down buttons. The pointer path is left for a later change. I still have not dragged in a real browser.
Try: open /trevato/forge/next on the preview and drag a row down the list; the dashed line moves with it, and a successful drop does not flicker back.
Earlier asks are met. A successful drop keeps the dragged order until the reload, and only a refusal restores it. The drag handlers are now tested through the real KEYS_JS in happy-dom, which is already a dependency. The reversed-order test uses one median on both sides. data-capacity is emitted only when the server draws a line. The touch note is in docs/design/areas/next-up.md. I could not diff against 39063e3ede46 because the clone does not have that commit, so I read the change as listed. The preview answers 200. The no-script page is unchanged. .next-week changes opacity only, so nothing shifts at rest.
Next, two things I would do:
- Non-blocking: add a pointer path so a row can be dragged on a phone. Today a phone uses the Top/Up/Down buttons.
- Non-blocking: extend the drag to rows past the top ten on ?all=1.
On box 1, the rows carry the estimate mid rather than the upper bound the issue names. That matches how the server fits the line, so the shown line equals the saved one.
The cut line on Next up now moves while a row is dragged over the list, before the save. I did not exercise the drag handlers in a browser or in a test.
Checked:
bun x tsc --noEmit,bun scripts/design-lint.ts, andweek-doors,week-sprintandweektests (38 pass).bun scripts/gate.tsprinted nothing, so I can't confirm it passed.data-cost, its estimate's mid, not the upper bound. The server's line is built on mid (cutLine→fitting(…, e => e.mid, …)), so using the upper bound would have drawn a different line than the one saved. A row with no estimate carries the median mid, as the server counts it.data-capacitywith the week's remaining dollars. It is left off when the server draws no line, so the page is unchanged then..next-week, opacity only, so no layout shift). The line's wording matches the server's.FIT_COUNT_JSinsrc/flow/week.ts, injected into the page script.rowCoststhere supplies the per-row numbers. The new test intest/week-doors.test.tsreads those attributes from the rendered page and evaluates the client rule. It checks the result equalsweekView'scut.fits.length, and that a reversed order matchescutLine..next-weekis named indocs/design/areas/next-up.md.Left out: the drag does not reach rows past the top ten (
?all=1is unchanged), and I added no keyboard-move preview, since keys post at once. With no script the page is as today.Closes #8858