Explore: integrate the approved WS3 design into the running app - #286
Merged
Merged
Conversation
…side plot ViewerComponent gains channelColors, forwarded via initGraph/updateChannels; EEGViewer keeps fixed colors instead of recoloring by quality, rescales a live view without clearing it, and draws the end pill inside the plot box.
…copy FixturePlot becomes SnapshotPlot (it now draws real frozen snapshots too). ExploreSurface, BlinkLessonView and EyesClosedView take a livePlot slot, a legend and the device channel list; review segments and step 4's range are props. Quality copy and summarizeQuality move to Explore/quality.ts, adjust names the BAD sensors. LessonPicker starts a lesson by id. Adds CleanSignalView and its story at 1366x768 and 1280x720.
EXPLORE_LESSONS gains 'Eyes-closed activity · about 1 minute'; the noise lesson is the four blink steps. The picker lays the three cards out in one row.
ExploreLessonFlow now runs the cleaner-signal tips (tip n of 3), the blink steps 0-4 and the separate eyes-closed activity in CleanSignalView, BlinkLessonView and EyesClosedView, with one live ViewerComponent per lesson in stable channel colors (quality colors for the tips). Step 4's range drives the live scale until five still seconds freeze the comparison. Eyes-closed: Begin unlocks audio and checks stream freshness, a 3-2-1 component timer, the chime-bounded interval, then 3 s eyes-open/eyes-closed review segments and the posterior rhythm ratio. NOISE_LESSON and the local FrozenStrip go.
Disconnected → ExploreDisconnected; connected → ExploreSurface with the live ViewerComponent (quality colors), the legend from deviceInfo, the quality summary from summarizeQuality, and ErrorBanners for a stopped stream (error or complete → Reconnect headset) and missing frontal channels (Switch headset). Explore's own Disconnect button goes; the shell chip's setup dialog owns it next. LiveSignalPrep is unchanged.
The shell device chip opens HeadsetSetupDialog; its Connected screen now has a secondary Disconnect beside Check my signal, which dispatches DisconnectFromDevice and closes the dialog. The Connected story shows it.
The blink comparison strips and the eyes-closed review caption now read "your recent signal, paused" / "your eyes-closed interval, paused" and the review card drops the live dot (PlotCard paused). No UI copy says frozen.
Found in the Electron playtest: feeding the head diagram of(sample) with a per-render channels array reset every electrode to grey on each epoch. ExploreSurface and CleanSignalView now take the shared stream (head) and a stable channels list, as the old screen wired them.
… cuts The stream-stopped banner now sits above both the surface and a running lesson (one wrapper), with a test that a stream ending mid-lesson shows it. Step 4 shows only its prompt until the paused strips appear. Drops the unused ExploreStatus.baselineStable and FrozenComparison.sharedScale fields, un-exports CleanSignalViewProps, and removes stale comments and the obsolete end-pill learning.
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.
Summary
Replaces the running
/explorescreen with the approved Storybook design (#282). The landing, the connected surface (quality summary, head diagram and sensor card with shared hover, live plot legenddeviceInfo.name · samplingRate Hz, three lesson cards), the cleaner-signal tips, the blink lesson steps 0–4, the separate eyes-closed activity, and the stream-stopped and unsupported-channel banners now all render the design's views with live data. Disconnect moves to the shell chip's setup dialog.Decisions followed
FixturePlotis renamed toSnapshotPlot.ExploreSurface,BlinkLessonViewandEyesClosedView(and the newCleanSignalView) take alivePlotslot. The runtime passesViewerComponent; stories passSnapshotPlot. The blink strips, the review segments and the example still renderSnapshotPlot. Waiting isquality === 'waiting'. The runner's localFrozenStripis deleted.channelColor(name, deviceInfo.channels)(channelColorshelper intraceColors.ts). The surface and the tips lesson keep the quality colors.ViewerComponent.channelColorsis forwarded throughinitGraph.channelColoursandupdateChannels(whose payload is now{channels, channelColours?}). When it is set,EEGViewer.updateDatadoes not recolor.height - LABEL_HEIGHT - 4. Fixed colors are honored. A liveamplitudeScalerescales in place:updateSnapshot(null, scale)on a live viewer redraws without clearing the data, andViewerComponentresends it when the scale changes. There is one new test for each of the three.EXPLORE_LESSONSgains "Eyes-closed activity · about 1 minute".LessonPicker.onStart(id). The picker uses a 3-column row.CLEAN_SIGNAL_LESSONis untouched.CleanSignalViewusesLessonStepPanelwith a newunitprop, so the label reads "How do I get a cleaner signal? · Tip n of 3", with pips, Back/Next/Exit and "Finish lesson". It sits beside the live plot (quality colors) and the head diagram. It has aCleanSignalTipsstory at 1366×768 and aCleanSignalTips720story.status().blinkEvents, labelled "blink · from your eyes, not your brain".supported.notDetectedshows after 8 s on steps 1–3 with no blink since the step began.viewer:navigatehandling is unchanged.LessonAudio.preview()inside the click.start(onStart, onEnd)setscuethroughsampleTimeAt. The end phase shows "Open your eyes".latestTime ≥ cue.endTime. Eyes-open is[start−3000, start]and eyes-closed is the middle 3 s of the interval. It uses the posterior channels present, withrhythmRatio = alphaRatio(start, end).showExampleis on for intro and review. Back re-runs from intro.Explore/quality.tsholds the copy,QUALITY_STATE_TONE,qualityCopy(adjust names the BAD sensors viaIntl.ListFormat) andsummarizeQuality. The fixtures keep only the per-state sensors (QUALITY_SENSORS). There is a precedence test.errororcomplete, and its action runsDisconnectFromDevicethenopenHeadsetSetup(). "Unsupported channels" names the missing frontal channels, and its action is "Switch headset" →openHeadsetSetup().HeadsetSetupgets an outline "Disconnect" beside "Check my signal". It dispatchesDisconnectFromDeviceand closes the dialog. The Connected story getsonDisconnect. Explore's own button is gone.LiveSignalPrep,ExploreSession(only theFRONTAL/POSTERIORconstants are now exported),LessonAudio, theshareReplaybufferSize,SignalQualityIndicatorComponentandExploreSensorCard.ExploreLessonFlow.tsxwas rewritten in place, andNOISE_LESSONis deleted.Later user decision: captures are labelled as paused.
PlotCard paused).Deviations (and why)
session.comparison()produces the strips at the chosen range. If the comparison is null, the step stays live and "Finish lesson" still works; that is the "skip" (confirmed with Main).ExploreSurfaceandCleanSignalViewtakehead(the live stream) and a stablechannelslist, instead of the fixture-derivedof(qualitySample(sensors)). That derivation fed the sensor card fake metrics, and with a per-render channels array it reset every electrode to grey on each epoch (caught in the playtest; fixed in e798e23).legend,deviceChannels,segments,error,blinkCountandonRangeChange. These replace the fixture constants, which werePLOT_LEGEND,EXPLORE_CHANNELScolors, the fixture review segments and the annotation count.markerStylemoved to the stories'SnapshotPlot, since the runtime only uses bands.CLEAN_SIGNAL_LESSON[0]intro ("Improve the signal quality") is not shown; Explore shows the three tips only (decision 5 says Tip n of 3).!supported && missing.length, so a slow AF7/AF8 stream never gets an empty title.Verification
npm run typecheck→> tsc --noEmitwith no errors.npx vitest run src/renderer/components src/renderer/utils/eeg→Test Files 36 passed (36)/Tests 168 passed (168). Earlier runs while Electron and Storybook were loading the machine hit 5 s timeouts in unrelated Design/Clean/HeadsetSetup tests; they pass on an idle run.npx eslint <changed files>→0 errors, 11 warnings(pre-existing warning classes).EEGViewer.test.ts: end pill inside the plot box; fixed colors surviveupdateData; a liveamplitudeScalechanges the y span 4× without clearing. They fail on the old viewer.Explore/quality.test.ts: precedence and adjust copy.Domain/Explorestories plusHeadsetSetup/Connectedrender, with document overflow 0 at 1366×768 and 1280×720. The picker, the tips (TIP 1 OF 3), the adjust copy ("Adjust AF7 and TP10"), step 4 and the review were checked in the DOM./tmp/ws3-integration/profile, DevTools closed, window sized withsetContentSize, viewport overlay off. Results at both sizes:aria-pressed, and the sensor card help opens ("AF7 · LEFT FOREHEAD …").OscillatorNode.startrecorded 660 (preview), 660 (start), 880, 880 Hz.document.scrollingElementoverflow: 0 on every state at both sizes. The shell's inner scroller showed 26 px only while the AF7 help popover was open at 1280×720./tmp/ws3-integration/*-1366.pngand*-1280.png(01-disconnected, 02-waiting, 03-surface, 04-hover-af7, 05-tip1..3, 06-noise-step0..4, 06-noise-step4-live, 07-eyes-intro/countdown/interval/end/review, 08-chip-connected-dialog, 09-after-disconnect).Quality states: Waiting and Settling were seen live. Ready, Adjust and No signal are covered only by the
summarizeQualitytest and the stories; the fixture replay never produced them.Known ceilings
The playtest window was throttled, and the flags could not stop it. It was launched with
--disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling(confirmed inps), plussetBackgroundThrottling(false),prevent-app-suspensionand a temporaryNSAppSleepDisabled(removed afterwards). Once the window lost the front, macOS still slowed it to ~20 timer ticks/s and ~10 fps rAF, so the fixture (onesetIntervaltick per sample) streamed at roughly 1/12 of real time. These timings could not be trusted:Review segments with a ratio, and the step-4 strips, were not observed live; they are covered by the stories.
The fixture replay has no detected blinks: 0 bands on steps 1–4 at both sizes, so blink bands were not observed live. The band code path is
EEGViewerannotations, which are already tested.Other limits:
Review round (fix commit)
EEGExplorationComponent.test.tsxchecks that a stream completing mid-lesson shows the alert with "Reconnect headset"; the test fails without the fix.ExploreStatus.baselineStableandFrozenComparison.sharedScalepublic fields (the internal state and validation stay);CleanSignalViewPropsexport;plotHeight + 6learning.