Custom Experiments: Add sounds and fix minor TODOs - #244
Merged
Conversation
macOS had no Edit menu at all, so Cmd+V/C/X/Z were dead in every input. Adds role:'editMenu' to the Darwin and default templates, and an isEditable-gated context menu (cut/copy/paste/selectAll) in prod; the dev context menu gets the same items above Inspect element. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
fs:readImages has accepted .gif/.webp since the extname fix; the helper text still claimed png/jpg/jpeg only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
Each custom condition can now carry a folder of sounds (.mp3/.wav/.m4a/.ogg) alongside or instead of its image folder. Sounds play at trial onset — the same moment the EEG marker fires — via an <audio autoplay> element in a new custom-specific lab.js template (custom no longer borrows the Faces/Houses template; audio-only trials show a speaker glyph). Co-assignment defaults: one trial per image with sounds paired index-wise in alphabetical order (cycling when fewer sounds than images); audio-only conditions get one trial per sound; pairs shuffle as units. fs:readImages / fs:readAudioFiles now sort directory listings so pairing is deterministic across platforms. Plumbing: fs:readAudioFiles IPC channel (main/preload/d.ts/storage), FILE_TYPES.AUDIO_DIR dialog reusing the StimulusFileAccess allowlist, CSP media-src bwfile:, media.audio preload in ExperimentWindow, sound-folder picker in Conditions and a Sound column in Trials. Also removes the unused 2020 leftovers experiments/custom/experiment.js and utils/labjs/protocols/custom.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
- lab.js preloads options.media.audio via fetch(), which requires supportFetchAPI on the privileged scheme — without it every custom study failed to prepare with 'URL scheme "bwfile" is not supported' (images survived because Image() loading doesn't go through fetch). Registered bwfile with supportFetchAPI + corsEnabled, matching pyodide:. - StimuliDesignColumn kept the sound count only in component state, so it vanished on remount; it now re-reads the folder on mount/audioDir change. Also let shouldComponentUpdate see state changes — it previously blocked re-render on any setState. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
…sting) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
deleteWorkspaceDir was fired without await, so readWorkspaces() listed the directory before shell.trashItem finished — the deleted workspace lingered on the homepage and a second delete errored on the already-trashed dir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
Type-aware linting via projectService, scoped to src TS files (internals/ isn't in tsconfig.json). Catches the fire-and-forget async class that left deleted workspaces on the Home screen. warn severity for gradual adoption (~25 pre-existing hits, untouched); new intentional fire-and-forget calls are marked with void. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5
Moves the image-resolution tip into a small box with a working imageresizer.com link, and drops the sound/image pairing explanation from the Conditions instructions per review feedback. Adds a setWindowOpenHandler so target="_blank" links actually open (Electron denies window.open by default). Keeps the filePaths[0] index access in loadDialog, silencing prefer-destructuring instead of rewriting it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YV9RAfwYDcTsLRyyCtZBpN
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
Adds optional sound stimuli to custom experiments and closes out the top three TODOS.md items (restore / accept / allow), plus fixes found during QA.
Sound stimuli
.mp3/.wav/.m4a/.ogg) next to its image folder. Sounds play at trial onset — the same moment the EEG marker fires.experiments/custom/experiment.ts) instead of borrowing Faces/Houses; renders<img>and/or<audio autoplay>per trial.fs:readAudioFilesIPC,FILE_TYPES.AUDIO_DIRdialog on the sameStimulusFileAccessallowlist,media-src bwfile:CSP,media.audiopreload, Sound column in Trials.TODO items closed
custom/experiment.js,protocols/custom.ts). Human click-through + Muse ERP remain (re-scoped in TODOS.md).role: 'editMenu'on macOS + default menus; right-click cut/copy/paste on editable fields in prod.QA fixes (from live testing)
bwfilescheme registered withoutsupportFetchAPIbroke every custom study at prepare (lab.js preloads audio viafetch(); images dodged it viaImage()).shouldComponentUpdateignored state).deleteWorkspaceDirracedreadWorkspaces().Guardrail
@typescript-eslint/no-floating-promises(warn, type-aware, src-scoped) — catches the unawaited-async bug class above. ~25 pre-existing hits left untouched (out of scope).Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_01ERuqciorhJChN9btAczwj5