LearnNewsExamplesServices
Frontmatter
id19580
titleFilm signature: a real diagonal drag scrolls the matrix to its canvas columns
stateClosed
labels
enhancementaidashboard
assigneesneo-fable
createdAtOct 11, 2026, 1:41 AM
updatedAtOct 11, 2026, 3:01 AM
githubUrlhttps://github.com/neomjs/neo/issues/19580
authorneo-fable
commentsCount0
parentIssue15252
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 11, 2026, 2:52 AM

Film signature: a real diagonal drag scrolls the matrix to its canvas columns

Closed v13.2.0/archive-v13-2-0-chunk-24 enhancementaidashboard
neo-fable
neo-fable commented on Oct 11, 2026, 1:41 AM

Context

The operator, 2026-10-11 00:0xZ, on the take at a852b8ca6e: the Workstation's 100k-row grid has columns at its right edge that hold offscreen canvases, never visible in the video, and the grid never gets scrolled — not vertically, not horizontally, and not diagonally by drag, which is the grid's own feature. The film's signature today is a scroll cue that sets the view's scrollTop in one step: a jump, not a motion, and the sparkline column (type: 'sparkline', the framework-owned OffscreenCanvas path) stays out of frame because the matrix pane is ≈ 300 px wide against ≈ 1080 px of columns.

Design authority: the operator's note (a peer design input, derived here: the close's claim is "a hundred thousand rows cross their midpoint… the content never stopped living" — a visible diagonal drag through the rows into the canvas columns is that claim as motion); the 2026-09-23 pacing rule (a real gesture at hand tempo, never a held frame).

The Problem

The grid's Neo.grid.ScrollManager has dragScroll: true by default: the main-thread addon GridDragScroll scrolls the body in both axes while the pointer drags it (neo-mouse-drag-scroll). Nothing in the tour exercises it: the dense tour and the film only scroll by scrollTop. The film's close therefore shows a static grid (after the sibling reveal leaf) with a jump, and the offscreen-canvas column — one of the grid's strongest demonstrations — never appears.

The Architectural Reality

  • apps/workstation/tour/GestureDriver.mjs executeResizeStep drives a real pointer drag through run.service.driveDrag({source, destination: {deltaX, deltaY}, durationMs, steps}) with the film cursor riding the drive's clock — the pattern a grid drag can reuse on the grid body (pane.view / the body element of Workspace.paneCache.scale).
  • apps/workstation/view/TourController.mjs executeCue + cueVocabulary.mjs WORKSTATION_CUE_TYPES + fiveBeatFilm.mjs filmCueTypes (the unit spec asserts the lists agree).
  • apps/workstation/view/ScalePane.mjs: columns #, Work item, State, Throughput, Pulse (animatedChange), progress, sparkline (160 px, OffscreenCanvas), a component column; rowHeight: 50; body.bufferColumnRange: 2.
  • The film witness (WorkstationFilmTourNL.spec.mjs) asserts one settled receipt per cue in screenplay order; the receipt of a new cue must carry its proof (scroll deltas in both axes).

The Fix

A grid-drag-scroll cue: {type: 'grid-drag-scroll', paneId: 'scale', deltaX, deltaY, strokes, options: {moveDelay: 33, moveSteps, showCursor: true}} — the executor measures the grid view's rect, presses inside it and drives diagonal strokes at hand tempo as raw mouse-event brackets (driveDrag arms Neo drag sensors only and refuses the grid, whose drag-scroll is a plain DOM listener), three strokes because the pane is narrower than the matrix, the hand pausing and pressing again at the anchor between them; it returns the receipt of the Contract Ledger below, applied only when the scroll settled and both dragged axes reached their expected travel. In film-signature, after the reveal and the jump to the middle (M08 keeps its cue): the diagonal drag carries the view down and to the right until the sparkline column is on screen, then the readout and the living close. Unit spec: the cue in both lists, the screenplay validates; the film witness: the receipt's deltas (vertical and horizontal both non-zero).

Contract Ledger

Cue {type: 'grid-drag-scroll', paneId, deltaX, deltaY, anchor?, strokes?, options?} — consumed by TourController#executeCue → GestureDriver#executeGridDragScrollStep(step, options); listed in WORKSTATION_CUE_TYPES and filmCueTypes.

  • paneId (String, required): a rendered grid pane in the workspace's pane cache ('scale').
  • deltaX, deltaY (Number, CSS px, at least one non-zero): pointer travel per stroke; negative drags the content left / up, so scrollLeft / scrollTop grow.
  • anchor ({x, y} fractions of the view rect, default 0.5 / 0.5): where each stroke presses.
  • strokes (positive integer, default 1): strokes from the anchor; the hand lifts between them.
  • options.moveDelay (ms between samples, default 16), options.moveSteps (samples per stroke, default 12), options.showCursor (the film cursor, default false), options.attempts (16 ms settle polls after the last release, default 180).

Input per stroke: mousedown on the grid body, moveSteps × mousemove on document.body, mouseup — raw events through InteractionService#simulateEvent, each sample dispatched after the previous one's acknowledgement plus moveDelay; the film cursor moves to a sample as that sample is dispatched, never ahead of an unacknowledged one.

Receipt {applied, errors, proof} with proof = {before, after: {scrollLeft, scrollTop, settled}, dispatched (samples per stroke), strokes, travel} and travel = {deltaX, deltaY, requested, room, expected, observed, slack, met}, the last six as {x, y}:

  • requested = |delta| × strokes; room = the scroll room left from before (the last column position against the body's container width; row count × row height against the available height), null when the grid has not measured that side; expected = min(requested, room), or requested when the room is unmeasured; observed = the scroll change projected on the dragged direction (the inertia tail may add, a bound clips); slack = one sample per stroke.
  • applied: true only when the pane is rendered, every sample was dispatched, the scroll settled (two reads agreeing over six polls within attempts), and every dragged axis moved the dragged way with observed ≥ expected − slack.
  • Failures by name (applied: false, errors, the proof retained once sampling began): no rendered pane or geometry; a refused sample; a tail unsettled within attempts × 16 ms; travel short of the expected.

Acceptance Criteria

  • grid-drag-scroll is handled by TourController#executeCue, listed in both cue vocabularies, and the screenplay validates (fiveBeatFilm.spec).
  • The headless film witness's receipt for the cue shows the tail settled and, per dragged axis, observed ≥ expected − slack with expected = min(requested, room) — three strokes of (−260, −120) at ≈ 33 ms samples (≈ 1.2 s per stroke): scrollTop grows by at least 360 less the slack, scrollLeft by at least min(780, room) less the slack, each axis with at least a row / a column of room; a direction-only or pixel-sized travel fails the witness. (Reconciled with the shipped shape: three raw-event strokes, not one driveDrag motion — see the Contract Ledger.)
  • Post-merge, deferred to #15252 (not demanded before this merge): the take's frames during the close show the matrix rows moving and the sparkline (canvas) column entering the frame. — Observed: the re-take 2026-10-11T00-55-16-516Z at dev 00b6f2aa6a (the #15252 re-take comment of 2026-10-11 01:01Z): three strokes 90.00–95.06 s pan the matrix through State → Throughput → Pulse → Load → Living signal (the canvas sparklines, in frame by ≈ 93 s) → Action; the rows cross throughout.

Out of Scope

  • Bringing the matrix and the feed in front before the close (the sibling leaf, filed first).
  • Any engine change to the grid or its drag-scroll addon.

Related

#15252 (the film epic) · the sibling reveal leaf · #19568 / #19571 (the merged film leaves of 2026-10-10)

Live latest-open sweep: the latest 20 open issues at 23:4xZ, no equivalent. A2A: no claim (the operator's note in chat is the source). MC sweep: "film signature scroll hidden behind the active Security tab; matrix inactive at the close", 5 results, no prior decision. Own-assignment sweep: 2 open (#15252 parent, #19382), none overlapping.

Origin Session ID: 80962da3-e567-4787-99b5-22455660843c

Retrieval Hint: "film signature grid-drag-scroll cue diagonal drag sparkline canvas column GridDragScroll driveDrag"

tobiu referenced in commit 00b6f2a - "feat(workstation): the film's close pans the matrix by real diagonal strokes into its canvas columns (#19580) (#19582) on Oct 11, 2026, 2:52 AM
tobiu closed this issue on Oct 11, 2026, 2:52 AM