Epoch reviewer Phase 0: ship epoch arrays to a static Canvas renderer#223
Open
jdpigeon wants to merge 2 commits into
Open
Epoch reviewer Phase 0: ship epoch arrays to a static Canvas renderer#223jdpigeon wants to merge 2 commits into
jdpigeon wants to merge 2 commits into
Conversation
added 2 commits
July 7, 2026 08:47
Resolves the forge-blocking open questions and writes a forge-ready Phase 0/1 execution plan into docs/epoch-review-ui-plan.md §0: - OQ1 rendering → Canvas 2D + DOM/SVG overlay, thin drawEpochs boundary, viewport windowing; WebGL a later swap. - OQ2 transport → extend the dataKey pattern (epochArrays, applyRejection); defer the runPython RPC (matches TODOS). - OQ6 live ERP preview → in scope for v1 (Phase 1), computed CLIENT-SIDE over the Phase-0 buffer (pure computeErp mean, zero worker round-trips) — Flavor 1 (while-cleaning) only; real-time-during-experiment is a separate future non-Python effort. - Shared condition-color palette built in Phase 0 (used by Phase-0 render, so not dead code). - Narrow Pyodide buffer-path fidelity test added in Phase 0. - Delivery split into two sequential PRs. Also updates the standing Pyodide-fidelity TODO to note the narrow test.
First half of the interactive epoch-review UI (docs/epoch-review-ui-plan.md
§0/§9). Read-only this phase — proves the data path and the Canvas 2D
rendering choice; click-to-reject, apply, and live ERP are Phase 1.
Data path (all new, extends the proven dataKey pattern — no runPython RPC):
Load Dataset → loadEpochsEpic → requestEpochArrays('raw_epochs')
→ worker get_epochs_arrays() writes a float32 buffer to MEMFS + returns meta
→ worker posts { buffer, results, dataKey:'epochArrays' } zero-copy transferable
→ pyodideMessageEpic → SetEpochArrays → reducer → CleanComponent → EpochReviewer
- utils.py: get_epochs_arrays(epochs, out_path) → float32 C-order
[epoch][channel][time] buffer + metadata (Marker/stim channel excluded via
pick_types(eeg=True)); plot_topo gains a palette param; condition-color
fallback DRYed to one _DEFAULT_CONDITION_PALETTE constant.
- webworker.js: readFileAfter read-back unified to post { buffer, results }
(savedEpochs write-back from PR #222 migrated to the same field).
- conditionPalette.ts: canonical condition palette, single source for the
Python matplotlib plots (JSON-injected) and the React reviewer.
- EpochReviewer.tsx: Canvas 2D traces (epochs side-by-side, channels stacked)
+ DOM overlay (labels, dividers), viewport windowing (first 8 epochs),
spike-preserving min/max downsampling, condition coloring, empty state, DPR
scaling. Pure decode/downsample helpers in epochArrays.ts.
Tests: native pytest (get_epochs_arrays buffer decodes to get_data(), Marker
excluded, byte length, event codes) + vitest (decode/downsample/color-index).
typecheck 0 · eslint 0 errors · vitest 37/37 · native pytest 10/10 · build green
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Phase 0 of the Interactive Epoch Review UI (
docs/epoch-review-ui-plan.md), forged from the plan locked via/plan-eng-review(§0). This half is read-only: it ships epoch data across the worker boundary and draws it, proving the data path and the Canvas 2D rendering choice. Click-to-reject, apply, and live ERP are Phase 1 (next PR).Locked architecture decisions this implements: OQ1 = Canvas 2D + DOM/SVG overlay, OQ2 = extend the
dataKeypattern (norunPythonRPC).Data path (all new)
Changes
utils.py—get_epochs_arrays(epochs, out_path)→ float32 C-order[epoch][channel][time]buffer + metadata (ch_namesEEG-only with the Marker/stim channel excluded viapick_types(eeg=True),sfreq,times,event_codes,drop_log).plot_topogains apaletteparam; the condition-color fallback is DRYed to one_DEFAULT_CONDITION_PALETTE.webworker.js— thereadFileAfterread-back is unified to post{ buffer, results }; the PR Fix broken Clean→Analyze round-trip: persist cleaned epochs to host disk #222savedEpochswrite-back migrated to the same field (verified still working).conditionPalette.ts(new) — canonical condition palette, single source for both the Python matplotlib plots (JSON-injected intoplot_topo/plot_conditions) and the React reviewer.SetEpochArraysaction +EpochArraysMetatype + reducer state (reset on experiment cleanup).EpochReviewer.tsx(new) — Canvas 2D traces (epochs side-by-side, channels stacked) + DOM overlay (channel/epoch labels, dividers), viewport windowing (first 8 epochs), spike-preserving min/max downsampling, condition coloring, empty state, devicePixelRatio scaling. Pure decode/downsample helpers inepochArrays.ts.Out of scope (Phase 1)
Click-to-reject,
apply_rejection, live ERP preview, scroll/scale navigation.Verification
npm run typecheck— 0 errorseslint(changed files) — 0 errors (1 pre-existing warning)npx vitest run— 37/37 pass (incl. 9 new decode/downsample/color-index cases)pytest tests/analysis/— 10/10 pass (4 new: buffer decodes toget_data(), Marker excluded, byte length, event codes; 6 existing, no regression)npm run build(electron-vite) — greenStacked on
epoch-cleaned-epochs-writeback(PR #222). 🤖 Generated with Claude Code