jspsych-tangram 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/construct/index.browser.js +4538 -3889
  2. package/dist/construct/index.browser.js.map +1 -1
  3. package/dist/construct/index.browser.min.js +13 -13
  4. package/dist/construct/index.browser.min.js.map +1 -1
  5. package/dist/construct/index.cjs +4 -7
  6. package/dist/construct/index.cjs.map +1 -1
  7. package/dist/construct/index.js +4 -7
  8. package/dist/construct/index.js.map +1 -1
  9. package/dist/index.cjs +332 -11
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.ts +180 -8
  12. package/dist/index.js +333 -13
  13. package/dist/index.js.map +1 -1
  14. package/dist/nback/index.browser.js +17703 -0
  15. package/dist/nback/index.browser.js.map +1 -0
  16. package/dist/nback/index.browser.min.js +42 -0
  17. package/dist/nback/index.browser.min.js.map +1 -0
  18. package/dist/nback/index.cjs +395 -0
  19. package/dist/nback/index.cjs.map +1 -0
  20. package/dist/nback/index.d.ts +175 -0
  21. package/dist/nback/index.js +393 -0
  22. package/dist/nback/index.js.map +1 -0
  23. package/dist/prep/index.browser.js +4538 -3891
  24. package/dist/prep/index.browser.js.map +1 -1
  25. package/dist/prep/index.browser.min.js +13 -13
  26. package/dist/prep/index.browser.min.js.map +1 -1
  27. package/dist/prep/index.cjs +5 -10
  28. package/dist/prep/index.cjs.map +1 -1
  29. package/dist/prep/index.js +5 -10
  30. package/dist/prep/index.js.map +1 -1
  31. package/package.json +9 -3
  32. package/src/index.ts +2 -1
  33. package/src/plugins/tangram-nback/NBackApp.tsx +316 -0
  34. package/src/plugins/tangram-nback/index.ts +141 -0
  35. package/tangram-construct.min.js +13 -13
  36. package/tangram-nback.min.js +42 -0
  37. package/tangram-prep.min.js +13 -13
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { JsPsychPlugin, ParameterType, JsPsych, TrialType } from 'jspsych';
2
2
 
3
- declare const info$1: {
3
+ declare const info$2: {
4
4
  name: string;
5
5
  version: string;
6
6
  parameters: {
@@ -109,7 +109,7 @@ declare const info$1: {
109
109
  };
110
110
  citations: string;
111
111
  };
112
- type Info$1 = typeof info$1;
112
+ type Info$2 = typeof info$2;
113
113
  /**
114
114
  * **tangram-construct**
115
115
  *
@@ -118,7 +118,7 @@ type Info$1 = typeof info$1;
118
118
  * @author Justin Yang & Sean Paul Anderson
119
119
  * @see {@link https://github.com/cogtoolslab/tangram_construction.git/tree/main/experiments/jspsych-tangram-prep}
120
120
  */
121
- declare class TangramConstructPlugin implements JsPsychPlugin<Info$1> {
121
+ declare class TangramConstructPlugin implements JsPsychPlugin<Info$2> {
122
122
  private jsPsych;
123
123
  static info: {
124
124
  name: string;
@@ -234,10 +234,10 @@ declare class TangramConstructPlugin implements JsPsychPlugin<Info$1> {
234
234
  * Launches the trial by invoking startConstructionTrial
235
235
  * with the display element, parameters, and jsPsych instance.
236
236
  */
237
- trial(display_element: HTMLElement, trial: TrialType<Info$1>): void;
237
+ trial(display_element: HTMLElement, trial: TrialType<Info$2>): void;
238
238
  }
239
239
 
240
- declare const info: {
240
+ declare const info$1: {
241
241
  name: string;
242
242
  version: string;
243
243
  parameters: {
@@ -308,7 +308,7 @@ declare const info: {
308
308
  };
309
309
  citations: string;
310
310
  };
311
- type Info = typeof info;
311
+ type Info$1 = typeof info$1;
312
312
  /**
313
313
  * **tangram-prep**
314
314
  *
@@ -316,7 +316,7 @@ type Info = typeof info;
316
316
  *
317
317
  * @author Justin Yang & Sean Paul Anderson
318
318
  */
319
- declare class TangramPrepPlugin implements JsPsychPlugin<Info> {
319
+ declare class TangramPrepPlugin implements JsPsychPlugin<Info$1> {
320
320
  private jsPsych;
321
321
  static info: {
322
322
  name: string;
@@ -394,7 +394,179 @@ declare class TangramPrepPlugin implements JsPsychPlugin<Info> {
394
394
  * Launches the trial by invoking startPrepTrial
395
395
  * with the display element, parameters, and jsPsych instance.
396
396
  */
397
+ trial(display_element: HTMLElement, trial: TrialType<Info$1>): void;
398
+ }
399
+
400
+ declare const info: {
401
+ name: string;
402
+ version: string;
403
+ parameters: {
404
+ /** Single tangram specification to display */
405
+ tangram: {
406
+ type: ParameterType;
407
+ default: undefined;
408
+ description: string;
409
+ };
410
+ /** Whether this trial is a match (for computing accuracy) */
411
+ isMatch: {
412
+ type: ParameterType;
413
+ default: undefined;
414
+ description: string;
415
+ };
416
+ /** Whether to show tangram decomposed into individual primitives with borders */
417
+ show_tangram_decomposition: {
418
+ type: ParameterType;
419
+ default: boolean;
420
+ description: string;
421
+ };
422
+ /** HTML content to display above the tangram as instructions */
423
+ instructions: {
424
+ type: ParameterType;
425
+ default: string;
426
+ description: string;
427
+ };
428
+ /** Text to display on response button */
429
+ button_text: {
430
+ type: ParameterType;
431
+ default: string;
432
+ description: string;
433
+ };
434
+ /** Duration to display tangram and accept responses (milliseconds) */
435
+ duration: {
436
+ type: ParameterType;
437
+ default: number;
438
+ description: string;
439
+ };
440
+ /** Callback fired when trial ends */
441
+ onTrialEnd: {
442
+ type: ParameterType;
443
+ default: undefined;
444
+ description: string;
445
+ };
446
+ };
447
+ data: {
448
+ /** Whether participant clicked the response button before duration expired */
449
+ responded_match: {
450
+ type: ParameterType;
451
+ description: string;
452
+ };
453
+ /** Reaction time in milliseconds (NaN if no response or response after duration) */
454
+ rt: {
455
+ type: ParameterType;
456
+ description: string;
457
+ };
458
+ /** Accuracy: 1 if correct, 0 if incorrect, NaN if isMatch not provided */
459
+ accuracy: {
460
+ type: ParameterType;
461
+ description: string;
462
+ };
463
+ /** Whether response occurred after duration expired */
464
+ responded_after_duration: {
465
+ type: ParameterType;
466
+ description: string;
467
+ };
468
+ /** Time of late response (NaN if no late response) */
469
+ rt_after_duration: {
470
+ type: ParameterType;
471
+ description: string;
472
+ };
473
+ };
474
+ citations: string;
475
+ };
476
+ type Info = typeof info;
477
+ /**
478
+ * **tangram-nback**
479
+ *
480
+ * A jsPsych plugin for n-back matching trials displaying a single tangram
481
+ * with a response button.
482
+ *
483
+ * @author Justin Yang & Sean Paul Anderson
484
+ * @see {@link https://github.com/cogtoolslab/tangram_construction.git/tree/main/experiments/jspsych-tangram-prep}
485
+ */
486
+ declare class TangramNBackPlugin implements JsPsychPlugin<Info> {
487
+ private jsPsych;
488
+ static info: {
489
+ name: string;
490
+ version: string;
491
+ parameters: {
492
+ /** Single tangram specification to display */
493
+ tangram: {
494
+ type: ParameterType;
495
+ default: undefined;
496
+ description: string;
497
+ };
498
+ /** Whether this trial is a match (for computing accuracy) */
499
+ isMatch: {
500
+ type: ParameterType;
501
+ default: undefined;
502
+ description: string;
503
+ };
504
+ /** Whether to show tangram decomposed into individual primitives with borders */
505
+ show_tangram_decomposition: {
506
+ type: ParameterType;
507
+ default: boolean;
508
+ description: string;
509
+ };
510
+ /** HTML content to display above the tangram as instructions */
511
+ instructions: {
512
+ type: ParameterType;
513
+ default: string;
514
+ description: string;
515
+ };
516
+ /** Text to display on response button */
517
+ button_text: {
518
+ type: ParameterType;
519
+ default: string;
520
+ description: string;
521
+ };
522
+ /** Duration to display tangram and accept responses (milliseconds) */
523
+ duration: {
524
+ type: ParameterType;
525
+ default: number;
526
+ description: string;
527
+ };
528
+ /** Callback fired when trial ends */
529
+ onTrialEnd: {
530
+ type: ParameterType;
531
+ default: undefined;
532
+ description: string;
533
+ };
534
+ };
535
+ data: {
536
+ /** Whether participant clicked the response button before duration expired */
537
+ responded_match: {
538
+ type: ParameterType;
539
+ description: string;
540
+ };
541
+ /** Reaction time in milliseconds (NaN if no response or response after duration) */
542
+ rt: {
543
+ type: ParameterType;
544
+ description: string;
545
+ };
546
+ /** Accuracy: 1 if correct, 0 if incorrect, NaN if isMatch not provided */
547
+ accuracy: {
548
+ type: ParameterType;
549
+ description: string;
550
+ };
551
+ /** Whether response occurred after duration expired */
552
+ responded_after_duration: {
553
+ type: ParameterType;
554
+ description: string;
555
+ };
556
+ /** Time of late response (NaN if no late response) */
557
+ rt_after_duration: {
558
+ type: ParameterType;
559
+ description: string;
560
+ };
561
+ };
562
+ citations: string;
563
+ };
564
+ constructor(jsPsych: JsPsych);
565
+ /**
566
+ * Launches the trial by invoking startNBackTrial
567
+ * with the display element, parameters, and jsPsych instance.
568
+ */
397
569
  trial(display_element: HTMLElement, trial: TrialType<Info>): void;
398
570
  }
399
571
 
400
- export { TangramConstructPlugin, TangramPrepPlugin };
572
+ export { TangramConstructPlugin, TangramNBackPlugin, TangramPrepPlugin };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ParameterType } from 'jspsych';
2
- import React from 'react';
2
+ import React, { useRef, useState, useEffect } from 'react';
3
3
  import { createRoot } from 'react-dom/client';
4
4
  import { v4 } from 'uuid';
5
5
 
@@ -12,9 +12,8 @@ const CONFIG = {
12
12
  completion: { fill: "#ccfff2", stroke: "#13da57" },
13
13
  silhouetteMask: "#374151",
14
14
  anchors: { invalid: "#7dd3fc", valid: "#475569" },
15
- piece: { draggingFill: "#8e7cc3ff", validFill: "#8e7cc3ff", invalidFill: "#ef4444", invalidStroke: "#dc2626", selectedStroke: "#674ea7", allGreenStroke: "#86efac", borderStroke: "#674ea7" },
16
- ui: { light: "#60a5fa", dark: "#1d4ed8" },
17
- blueprint: { fill: "#374151", selectedStroke: "#111827", badgeFill: "#000000", labelFill: "#ffffff" },
15
+ piece: { draggingFill: "#8e7cc3ff", validFill: "#8e7cc3ff", invalidFill: "#ef4444", allGreenStroke: "#86efac"},
16
+ blueprint: { fill: "#374151", badgeFill: "#000000", labelFill: "#ffffff" },
18
17
  tangramDecomposition: { stroke: "#fef2cc" }
19
18
  },
20
19
  opacity: {
@@ -25,7 +24,7 @@ const CONFIG = {
25
24
  piece: { invalid: 0.35, dragging: 0.75, locked: 1, normal: 1 }
26
25
  },
27
26
  size: {
28
- stroke: { bandPx: 5, pieceSelectedPx: 3, allGreenStrokePx: 10, pieceBorderPx: 2, tangramDecompositionPx: 1 },
27
+ stroke: { bandPx: 5, allGreenStrokePx: 10, tangramDecompositionPx: 1 },
29
28
  anchorRadiusPx: { valid: 1, invalid: 1 },
30
29
  badgeFontPx: 16,
31
30
  centerBadge: { fractionOfOuterR: 0.15, minPx: 20, marginPx: 4 }
@@ -45,9 +44,7 @@ const CONFIG = {
45
44
  },
46
45
  game: {
47
46
  snapRadiusPx: 15,
48
- showBorders: false,
49
- hideTouchingBorders: true
50
- }
47
+ showBorders: false}
51
48
  };
52
49
 
53
50
  function isComposite(bp) {
@@ -3561,7 +3558,7 @@ function startConstructionTrial(display_element, params, _jsPsych) {
3561
3558
  return { root, display_element, jsPsych: _jsPsych };
3562
3559
  }
3563
3560
 
3564
- const info$1 = {
3561
+ const info$2 = {
3565
3562
  name: "tangram-construct",
3566
3563
  version: "1.0.0",
3567
3564
  parameters: {
@@ -3675,7 +3672,7 @@ class TangramConstructPlugin {
3675
3672
  this.jsPsych = jsPsych;
3676
3673
  }
3677
3674
  static {
3678
- this.info = info$1;
3675
+ this.info = info$2;
3679
3676
  }
3680
3677
  /**
3681
3678
  * Launches the trial by invoking startConstructionTrial
@@ -3819,7 +3816,7 @@ function startPrepTrial(display_element, params, jsPsych) {
3819
3816
  return { root, display_element, jsPsych };
3820
3817
  }
3821
3818
 
3822
- const info = {
3819
+ const info$1 = {
3823
3820
  name: "tangram-prep",
3824
3821
  version: "1.0.0",
3825
3822
  parameters: {
@@ -3895,7 +3892,7 @@ class TangramPrepPlugin {
3895
3892
  this.jsPsych = jsPsych;
3896
3893
  }
3897
3894
  static {
3898
- this.info = info;
3895
+ this.info = info$1;
3899
3896
  }
3900
3897
  /**
3901
3898
  * Launches the trial by invoking startPrepTrial
@@ -3931,5 +3928,328 @@ class TangramPrepPlugin {
3931
3928
  }
3932
3929
  }
3933
3930
 
3934
- export { TangramConstructPlugin, TangramPrepPlugin };
3931
+ function startNBackTrial(display_element, params, _jsPsych) {
3932
+ const root = createRoot(display_element);
3933
+ root.render(React.createElement(NBackView, { params }));
3934
+ return { root, display_element, jsPsych: _jsPsych };
3935
+ }
3936
+ function NBackView({ params }) {
3937
+ const {
3938
+ tangram,
3939
+ isMatch,
3940
+ show_tangram_decomposition,
3941
+ instructions,
3942
+ button_text,
3943
+ duration,
3944
+ onTrialEnd
3945
+ } = params;
3946
+ const trialStartTime = useRef(Date.now());
3947
+ const buttonEnabledRef = useRef(true);
3948
+ const timeoutIdRef = useRef(null);
3949
+ const hasRespondedRef = useRef(false);
3950
+ const responseDataRef = useRef(null);
3951
+ const [buttonDisabled, setButtonDisabled] = useState(false);
3952
+ const CANON = /* @__PURE__ */ new Set([
3953
+ "square",
3954
+ "smalltriangle",
3955
+ "parallelogram",
3956
+ "medtriangle",
3957
+ "largetriangle"
3958
+ ]);
3959
+ const filteredTans = tangram.solutionTans.filter((tan) => {
3960
+ const tanName = tan.name ?? tan.kind;
3961
+ return CANON.has(tanName);
3962
+ });
3963
+ const mask = filteredTans.map((tan) => {
3964
+ const polygon = tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }));
3965
+ return polygon;
3966
+ });
3967
+ const primitiveDecomposition = filteredTans.map((tan) => ({
3968
+ kind: tan.name ?? tan.kind,
3969
+ polygon: tan.vertices.map(([x, y]) => ({ x: x ?? 0, y: -(y ?? 0) }))
3970
+ }));
3971
+ const DISPLAY_SIZE = 400;
3972
+ const viewport = {
3973
+ w: DISPLAY_SIZE,
3974
+ h: DISPLAY_SIZE
3975
+ };
3976
+ const scaleS = React.useMemo(() => {
3977
+ const u = inferUnitFromPolys$1(mask);
3978
+ return u ? CONFIG.layout.grid.unitPx / u : 1;
3979
+ }, [mask]);
3980
+ const centerPos = {
3981
+ cx: viewport.w / 2,
3982
+ cy: viewport.h / 2
3983
+ };
3984
+ const pathD = (poly) => {
3985
+ if (!poly || poly.length === 0) return "";
3986
+ const moves = poly.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`);
3987
+ return moves.join(" ") + " Z";
3988
+ };
3989
+ const endTrial = (data) => {
3990
+ if (timeoutIdRef.current) {
3991
+ clearTimeout(timeoutIdRef.current);
3992
+ timeoutIdRef.current = null;
3993
+ }
3994
+ const accuracy = isMatch !== void 0 ? isMatch === data.responded_match ? 1 : 0 : NaN;
3995
+ const trialData = {
3996
+ ...data,
3997
+ accuracy,
3998
+ tangram_id: tangram.tangramID,
3999
+ is_match: isMatch
4000
+ };
4001
+ if (onTrialEnd) {
4002
+ onTrialEnd(trialData);
4003
+ }
4004
+ };
4005
+ const handleButtonClick = () => {
4006
+ if (!buttonEnabledRef.current) {
4007
+ const rt_late = Date.now() - trialStartTime.current;
4008
+ hasRespondedRef.current = true;
4009
+ responseDataRef.current = {
4010
+ responded_match: true,
4011
+ rt: NaN,
4012
+ responded_after_duration: true,
4013
+ rt_after_duration: rt_late
4014
+ };
4015
+ endTrial(responseDataRef.current);
4016
+ } else {
4017
+ const rt = Date.now() - trialStartTime.current;
4018
+ buttonEnabledRef.current = false;
4019
+ setButtonDisabled(true);
4020
+ hasRespondedRef.current = true;
4021
+ responseDataRef.current = {
4022
+ responded_match: true,
4023
+ rt,
4024
+ responded_after_duration: false,
4025
+ rt_after_duration: NaN
4026
+ };
4027
+ }
4028
+ };
4029
+ useEffect(() => {
4030
+ timeoutIdRef.current = setTimeout(() => {
4031
+ buttonEnabledRef.current = false;
4032
+ if (hasRespondedRef.current && responseDataRef.current) {
4033
+ endTrial(responseDataRef.current);
4034
+ } else {
4035
+ endTrial({
4036
+ responded_match: false,
4037
+ rt: NaN,
4038
+ responded_after_duration: false,
4039
+ rt_after_duration: NaN
4040
+ });
4041
+ }
4042
+ }, duration);
4043
+ return () => {
4044
+ if (timeoutIdRef.current) {
4045
+ clearTimeout(timeoutIdRef.current);
4046
+ }
4047
+ };
4048
+ }, []);
4049
+ const renderSilhouette = () => {
4050
+ if (show_tangram_decomposition) {
4051
+ const rawPolys = primitiveDecomposition.map((primInfo) => primInfo.polygon);
4052
+ const placedPolys = placeSilhouetteGridAlignedAsPolys(rawPolys, scaleS, centerPos);
4053
+ return /* @__PURE__ */ React.createElement("g", { key: "sil-decomposed", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => /* @__PURE__ */ React.createElement(React.Fragment, { key: `prim-${i}` }, /* @__PURE__ */ React.createElement(
4054
+ "path",
4055
+ {
4056
+ d: pathD(scaledPoly),
4057
+ fill: CONFIG.color.silhouetteMask,
4058
+ opacity: CONFIG.opacity.silhouetteMask,
4059
+ stroke: "none"
4060
+ }
4061
+ ), /* @__PURE__ */ React.createElement(
4062
+ "path",
4063
+ {
4064
+ d: pathD(scaledPoly),
4065
+ fill: "none",
4066
+ stroke: CONFIG.color.tangramDecomposition.stroke,
4067
+ strokeWidth: CONFIG.size.stroke.tangramDecompositionPx
4068
+ }
4069
+ ))));
4070
+ } else {
4071
+ const placedPolys = placeSilhouetteGridAlignedAsPolys(mask, scaleS, centerPos);
4072
+ return /* @__PURE__ */ React.createElement("g", { key: "sil-unified", pointerEvents: "none" }, placedPolys.map((scaledPoly, i) => /* @__PURE__ */ React.createElement(
4073
+ "path",
4074
+ {
4075
+ key: `sil-${i}`,
4076
+ d: pathD(scaledPoly),
4077
+ fill: CONFIG.color.silhouetteMask,
4078
+ opacity: CONFIG.opacity.silhouetteMask,
4079
+ stroke: "none"
4080
+ }
4081
+ )));
4082
+ }
4083
+ };
4084
+ return /* @__PURE__ */ React.createElement("div", { style: {
4085
+ display: "flex",
4086
+ flexDirection: "column",
4087
+ alignItems: "center",
4088
+ justifyContent: "flex-start",
4089
+ minHeight: "100vh",
4090
+ padding: "40px 20px",
4091
+ background: "#f5f5f5"
4092
+ } }, instructions && /* @__PURE__ */ React.createElement(
4093
+ "div",
4094
+ {
4095
+ style: {
4096
+ maxWidth: "800px",
4097
+ width: "100%",
4098
+ marginBottom: "30px",
4099
+ textAlign: "center",
4100
+ fontSize: "18px",
4101
+ lineHeight: "1.5"
4102
+ },
4103
+ dangerouslySetInnerHTML: { __html: instructions }
4104
+ }
4105
+ ), /* @__PURE__ */ React.createElement("div", { style: {
4106
+ display: "flex",
4107
+ flexDirection: "column",
4108
+ alignItems: "center",
4109
+ gap: "30px"
4110
+ } }, /* @__PURE__ */ React.createElement(
4111
+ "svg",
4112
+ {
4113
+ width: viewport.w,
4114
+ height: viewport.h,
4115
+ viewBox: `0 0 ${viewport.w} ${viewport.h}`,
4116
+ style: {
4117
+ display: "block",
4118
+ background: CONFIG.color.bands.silhouette.fillEven,
4119
+ border: `${CONFIG.size.stroke.bandPx}px solid ${CONFIG.color.bands.silhouette.stroke}`,
4120
+ borderRadius: "8px"
4121
+ }
4122
+ },
4123
+ renderSilhouette()
4124
+ ), /* @__PURE__ */ React.createElement(
4125
+ "button",
4126
+ {
4127
+ className: "jspsych-btn",
4128
+ onClick: handleButtonClick,
4129
+ disabled: buttonDisabled,
4130
+ style: {
4131
+ padding: "12px 30px",
4132
+ fontSize: "16px",
4133
+ cursor: buttonDisabled ? "not-allowed" : "pointer",
4134
+ opacity: buttonDisabled ? 0.5 : 1
4135
+ }
4136
+ },
4137
+ button_text
4138
+ )));
4139
+ }
4140
+
4141
+ const info = {
4142
+ name: "tangram-nback",
4143
+ version: "1.0.0",
4144
+ parameters: {
4145
+ /** Single tangram specification to display */
4146
+ tangram: {
4147
+ type: ParameterType.COMPLEX,
4148
+ default: void 0,
4149
+ description: "TangramSpec object defining target shape to display"
4150
+ },
4151
+ /** Whether this trial is a match (for computing accuracy) */
4152
+ isMatch: {
4153
+ type: ParameterType.BOOL,
4154
+ default: void 0,
4155
+ description: "Whether this tangram matches the previous one (optional)"
4156
+ },
4157
+ /** Whether to show tangram decomposed into individual primitives with borders */
4158
+ show_tangram_decomposition: {
4159
+ type: ParameterType.BOOL,
4160
+ default: false,
4161
+ description: "Whether to show tangram decomposed into individual primitives with borders"
4162
+ },
4163
+ /** HTML content to display above the tangram as instructions */
4164
+ instructions: {
4165
+ type: ParameterType.STRING,
4166
+ default: "",
4167
+ description: "HTML content to display above the tangram as instructions"
4168
+ },
4169
+ /** Text to display on response button */
4170
+ button_text: {
4171
+ type: ParameterType.STRING,
4172
+ default: "Same as previous!",
4173
+ description: "Text to display on response button"
4174
+ },
4175
+ /** Duration to display tangram and accept responses (milliseconds) */
4176
+ duration: {
4177
+ type: ParameterType.INT,
4178
+ default: 3e3,
4179
+ description: "Duration in milliseconds to display tangram and accept responses"
4180
+ },
4181
+ /** Callback fired when trial ends */
4182
+ onTrialEnd: {
4183
+ type: ParameterType.FUNCTION,
4184
+ default: void 0,
4185
+ description: "Callback when trial completes with full data"
4186
+ }
4187
+ },
4188
+ data: {
4189
+ /** Whether participant clicked the response button before duration expired */
4190
+ responded_match: {
4191
+ type: ParameterType.BOOL,
4192
+ description: "True if participant clicked response button, false otherwise"
4193
+ },
4194
+ /** Reaction time in milliseconds (NaN if no response or response after duration) */
4195
+ rt: {
4196
+ type: ParameterType.INT,
4197
+ description: "Milliseconds between trial start and button click (NaN if no response or late response)"
4198
+ },
4199
+ /** Accuracy: 1 if correct, 0 if incorrect, NaN if isMatch not provided */
4200
+ accuracy: {
4201
+ type: ParameterType.FLOAT,
4202
+ description: "1 if response matches isMatch parameter, 0 otherwise (NaN if isMatch not provided)"
4203
+ },
4204
+ /** Whether response occurred after duration expired */
4205
+ responded_after_duration: {
4206
+ type: ParameterType.BOOL,
4207
+ description: "True if button clicked after duration expired, false otherwise"
4208
+ },
4209
+ /** Time of late response (NaN if no late response) */
4210
+ rt_after_duration: {
4211
+ type: ParameterType.INT,
4212
+ description: "Milliseconds between trial start and late button click (NaN if no late response)"
4213
+ }
4214
+ },
4215
+ citations: ""
4216
+ };
4217
+ class TangramNBackPlugin {
4218
+ constructor(jsPsych) {
4219
+ this.jsPsych = jsPsych;
4220
+ }
4221
+ static {
4222
+ this.info = info;
4223
+ }
4224
+ /**
4225
+ * Launches the trial by invoking startNBackTrial
4226
+ * with the display element, parameters, and jsPsych instance.
4227
+ */
4228
+ trial(display_element, trial) {
4229
+ const wrappedOnTrialEnd = (data) => {
4230
+ if (trial.onTrialEnd) {
4231
+ trial.onTrialEnd(data);
4232
+ }
4233
+ const reactContext = display_element.__reactContext;
4234
+ if (reactContext?.root) {
4235
+ reactContext.root.unmount();
4236
+ }
4237
+ display_element.innerHTML = "";
4238
+ this.jsPsych.finishTrial(data);
4239
+ };
4240
+ const params = {
4241
+ tangram: trial.tangram,
4242
+ isMatch: trial.isMatch,
4243
+ show_tangram_decomposition: trial.show_tangram_decomposition,
4244
+ instructions: trial.instructions,
4245
+ button_text: trial.button_text,
4246
+ duration: trial.duration,
4247
+ onTrialEnd: wrappedOnTrialEnd
4248
+ };
4249
+ const { root, display_element: element, jsPsych } = startNBackTrial(display_element, params, this.jsPsych);
4250
+ element.__reactContext = { root, jsPsych };
4251
+ }
4252
+ }
4253
+
4254
+ export { TangramConstructPlugin, TangramNBackPlugin, TangramPrepPlugin };
3935
4255
  //# sourceMappingURL=index.js.map