Skip to content

feat: import externally-authored jsPsych and lab.js experiments - #246

Open
jdpigeon wants to merge 3 commits into
mainfrom
jspsych-import
Open

feat: import externally-authored jsPsych and lab.js experiments#246
jdpigeon wants to merge 3 commits into
mainfrom
jspsych-import

Conversation

@jdpigeon

Copy link
Copy Markdown
Contributor

This PR adds an Import Experiment path that runs externally-authored jsPsych v8 timelines and lab.js .study.json files through the existing Collect → Clean → Analyze pipeline.

What changed

  • New experiment type: EXPERIMENTS.IMPORTED with an ImportedExperiment contract persisted in ExperimentParameters (kind, copied file path, conditionKey, correctKey, ordered conditionLabels[])
  • Runtime seam: renamed ExperimentWindowLabjsExperimentWindow, added ImportedExperimentWindow, both behind an ExperimentRuntime dispatcher so RunComponent and PreviewExperimentComponent stay runtime-agnostic
  • jsPsych host: owns the initJsPsych() call via window injection — merges display_element, override_safe_mode (required under file://), on_trial_start marker emission from resolved trial data, and on_finish → BrainWaves behavioral CSV
  • All 52 official @jspsych/plugin-* packages ship in the bundle — a missing plugin is a hard classroom failure, so the full set is included
  • Static scan of timeline files rejects jsPsych v6, surfaces candidate data keys/values, and names missing plugin globals
  • Asset URL rewriting to bwfile:// for offline stimulus serving
  • Markers tab in the Design screen lets the teacher pick which data key carries condition labels, confirm the label order (codes = index + 1), and add labels the scan missed. Empty labels = behavior-only with EEG forced off.
  • main-process file import copies the .js/.json into the workspace; asset folders stay in-place through the existing StimulusFileAccess allowlist
  • buildMarkerRegistryFromLabels + resolveMarkerRegistry in markerRegistry.ts feeds both collection (CSV + events sidecar) and MNE analysis (event_id)
  • Custom bank relabelled to "Experiment Builder" (enum value stays 'Custom' for backwards compat)

Testing

  • 168 tests passing across 29 files
  • npm run typecheck clean
  • npx eslint src/ clean
  • npm run build clean (main/preload/renderer + viewer)

Out of scope for this PR

  • @jspsych-contrib plugins
  • jsPsych extensions (case-by-case later)
  • Process isolation / iframe / webview (the <webview> path collides with EEG viewer preload wiring; kept as a one-file change for v2)
  • Hardware ERP timing validation (needs photodiode or Muse sample-clock measurement — noted in open questions)

Closes the jsPsych import items in TODOS.md and ROADMAP.md.

Add a full Import Experiment path that runs third-party jsPsych v8 timelines
and lab.js studies through the existing Collect → Clean → Analyze pipeline.

Key pieces:
- New EXPERIMENTS.IMPORTED type with ImportedExperiment contract persisted
  in ExperimentParameters (kind, file path, conditionKey, correctKey,
  conditionLabels[] ordered for stable numeric codes)
- Runtime seam: rename ExperimentWindow → LabjsExperimentWindow, add
  ImportedExperimentWindow, both behind an ExperimentRuntime dispatcher so
  RunComponent and PreviewExperimentComponent stay runtime-agnostic
- jsPsych host owns initJsPsych() via window injection: merges
  display_element, override_safe_mode, on_trial_start marker emission
  (requestAnimationFrame from resolved trial data), on_finish → BrainWaves
  behavioral CSV via normalize.ts
- All 52 official @jspsych/plugin-* packages ship in the bundle; missing
  plugins are a hard classroom failure
- Static scan.ts rejects v6, surfaces data keys/values and missing globals
- Asset URL rewriting to bwfile:// for offline stimulus serving
- Markers tab in ImportedDesignComponent: teacher declares condition key and
  confirms label order before first subject; empty labels = behavior-only
  with EEG forced off
- main-process file import copies the .js/.json into the workspace;
  asset folders are authorized in-place through existing StimulusFileAccess
- markerRegistry.ts gains buildMarkerRegistryFromLabels + resolveMarkerRegistry;
  used by both collection (CSV + events sidecar) and pyodideEpics (MNE event_id)
- Custom bank card relabelled to 'Experiment Builder' (enum value stays 'Custom')

Tests: 168 passing across 29 files. TypeScript, lint, and build clean.

Closes TODOS.md/ROADMAP.md items; updates .llms/learnings.md.
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.

1 participant