Skip to content

Feature/streaming view - #8

Closed
TitouanMasson wants to merge 5 commits into
simonavrillon:mainfrom
TitouanMasson:feature/streaming-view
Closed

TitouanMasson wants to merge 5 commits into
simonavrillon:mainfrom
TitouanMasson:feature/streaming-view

Conversation

@TitouanMasson

Copy link
Copy Markdown

Server-side edit session (float64 reference matrix) so heavy decomposition files can be opened, edited, and saved without hitting the browser's fetch limit or losing precision. Saved output is bit-identical to source for untouched motor units.

TitouanMasson and others added 5 commits September 23, 2026 13:46
… session

The editor's slowest path is a heavy decomposition file: the browser held
the whole pulse-train matrix, hit its own fetch size limit on large files,
and re-uploaded everything on save, going through float32 and silently
rounding 40-65% of values that were never touched.

This introduces EditSession: a float64 reference copy of the pulse trains
kept server-side for the life of an edit session, addressed by each motor
unit's stable id (not its position, which shifts when MUs are removed or
deduplicated), with a versioned row per recomputation and a small TTL
cache holding at most one session at a time.

/edit/load-by-path, given the X-Muedit-Lazy-Rows header, returns metadata
and a session token without the matrix when a session could be opened.
/edit/session/row serves one motor unit's row at a given revision, as raw
float64 - a display copy at full precision, not narrowed to float32, since
one row is at most tens of MB and costs nothing extra to widen. /edit/save
and /edit/update-filter read and write the session's rows instead of the
uploaded matrix when a session token is given; /edit/session/duplicate
registers a duplicated motor unit in the session so it can be saved from
there too. Every one of these falls back to the previous upload-based
behaviour when no session token is given, so nothing existing changes for
a file whose session cannot be opened.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
test_edit_fidelity.py establishes the contract that matters most: loading
a decomposition, editing one motor unit, and saving it back leaves every
other motor unit bit-identical to the source file, and removing a flagged
motor unit does the same for the ones that are kept.

test_edit_session.py covers EditSession itself (row versioning and
pruning, duplicate, assemble) and the API surface built on it: a session
token and motor-unit ids on load, a bit-identical save from a session, a
revision bump on update-filter with undo by revision number, duplicate and
removal going through the session, and the 404/409/400 responses for an
unknown session, a discarded revision, or a sample-count mismatch.

test_edit_lazy_rows.py covers the lazy load specifically: the matrix is
omitted while the session token and ids still come through, a fetched row
is bit-identical to the float64 source, a recomputed revision is
fetchable while revision zero is kept, and a ROI edit against the session
produces the same result as the same edit against an uploaded pulse
train.

test_api_http.py's route table and MELD-payload assertions are updated to
match: the new /edit/session/row and /edit/session/duplicate routes, and
the edit-load frame now carrying float64 samples instead of float32.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ole matrix

Adds a session token and a per-motor-unit revision map to the edit slice.
Loading, filter updates, and save send the session token and the current
revision when one is open, instead of the pulse-train matrix; undo,
reset, and duplicate now read and restore a revision number instead of a
copy of the pulse train, falling back to the previous behaviour when no
session is open.

In lazy mode pulseTrains starts as one empty placeholder per motor unit.
Showing a motor unit whose held row is stale fetches the current
revision from the session (as float64, not narrowed to float32 - the
extra bytes are negligible per row and this removes the last place a
displayed value could differ from what is on disk) and caches the four
most recently viewed rows, so switching back to a recent motor unit is
instant and memory stays bounded regardless of file size.

The pulse-train canvas draws a min/max envelope instead of every sample
once a series has several times more samples than the plot has pixel
columns: every peak stays visible and render cost now follows the width
of the plot instead of the length of the recording.

Also replaces a Math.max(0, ...spikes) that spread every discharge time
of every motor unit as call arguments before computing a max - on a large
decomposition this exceeded the engine's argument limit and threw before
the "Saving..." status was shown, so the save silently did nothing - with
a plain loop in both places that compute it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
binary-payloads.test.js: the edit-load (MELD) frame is now decoded as
float64, not float32; MDPV/decompose-preview stays float32 and is
unaffected. The shared test packer takes an element-width option so both
are covered from one helper.

editing-operations.test.js: adds a regression test for a real bug found
by manual testing and fixed in the previous commit - resetting a motor
unit right after duplicating a different one wiped the source motor
unit's row to nothing and left it stuck ("No data"), because the
lazy-mode placeholder array for restoring original pulse trains wasn't
sized per motor unit, so the duplicate's placeholder landed on motor
unit 0's slot, and reset treated whatever it found there - including an
empty placeholder - as real data to restore. Reproduced deterministically
outside the browser before fixing it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The local ESLint rule split the file path on "/" only. On Windows, ESLint
hands it a backslash-joined absolute path, so the rule took the whole
path as the "filename" and flagged every file, not just non-kebab-case
ones. Split on both separators instead.

Unrelated to the edit-session work above - found while running the
frontend checks on Windows for the first time, and included here so
`npm run check` is actually green on this branch rather than failing for
an unrelated pre-existing reason.

Co-Authored-By: Claude Sonnet 5 <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.

2 participants