@adriansteffan/reactive 0.0.43 → 0.0.44
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.
- package/.claude/settings.local.json +7 -1
- package/README.md +106 -3
- package/dist/{mod-D6W3wq3h.js → mod-D6DlS3ur.js} +6482 -6114
- package/dist/mod.d.ts +54 -2
- package/dist/reactive.es.js +42 -33
- package/dist/reactive.umd.js +38 -36
- package/dist/style.css +1 -1
- package/dist/{web-B1hJOwit.js → web-D7VcCd-t.js} +1 -1
- package/dist/{web-BYSmfdtR.js → web-o3I0sgwu.js} +1 -1
- package/package.json +1 -1
- package/src/components/canvasblock.tsx +112 -70
- package/src/components/checkdevice.tsx +15 -0
- package/src/components/enterfullscreen.tsx +5 -3
- package/src/components/exitfullscreen.tsx +4 -1
- package/src/components/experimentprovider.tsx +7 -2
- package/src/components/experimentrunner.tsx +66 -52
- package/src/components/microphonecheck.tsx +3 -0
- package/src/components/mobilefilepermission.tsx +3 -0
- package/src/components/plaininput.tsx +17 -0
- package/src/components/prolificending.tsx +3 -0
- package/src/components/quest.tsx +57 -0
- package/src/components/storeui.tsx +15 -11
- package/src/components/text.tsx +11 -0
- package/src/components/upload.tsx +56 -271
- package/src/mod.tsx +1 -0
- package/src/utils/bytecode.ts +50 -0
- package/src/utils/simulation.ts +268 -0
- package/src/utils/upload.ts +299 -0
- package/template/README.md +59 -0
- package/template/backend/src/backend.ts +1 -0
- package/template/package.json +2 -0
- package/template/simulate.ts +15 -0
- package/template/src/Experiment.tsx +58 -5
- package/template/src/main.tsx +1 -1
- package/template/tsconfig.json +2 -3
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
|
@@ -5,7 +5,13 @@
|
|
|
5
5
|
"Bash(npm run:*)",
|
|
6
6
|
"WebSearch",
|
|
7
7
|
"WebFetch(domain:github.com)",
|
|
8
|
-
"WebFetch(domain:surveyjs.io)"
|
|
8
|
+
"WebFetch(domain:surveyjs.io)",
|
|
9
|
+
"Read(//Users/adriansteffan/Projects/rtest/**)",
|
|
10
|
+
"Bash(npm link:*)",
|
|
11
|
+
"Bash(npx tsc:*)",
|
|
12
|
+
"Bash(grep:*)",
|
|
13
|
+
"Bash(cp:*)",
|
|
14
|
+
"Bash(cd:*)"
|
|
9
15
|
],
|
|
10
16
|
"deny": [],
|
|
11
17
|
"ask": []
|
package/README.md
CHANGED
|
@@ -42,6 +42,105 @@ Premade components available so far:
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
## Simulation
|
|
46
|
+
|
|
47
|
+
Reactive includes a simulation system that lets you run experiments headlessly, generating synthetic data from simulated participants. This is useful for some basic computational modeling, verifying data pipelines, and sample size planning.
|
|
48
|
+
|
|
49
|
+
### Quick start
|
|
50
|
+
|
|
51
|
+
Define your participant generator in `Experiment.tsx`:
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
export const simulationConfig = {
|
|
55
|
+
participants: {
|
|
56
|
+
generator: (i) => ({ id: i, nickname: `participant_${i}` }),
|
|
57
|
+
count: 10,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Run the simulation:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
npm run simulate
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This starts the backend, simulates all participants through the experiment, uploads data via the real backend (just like real participants would), and shuts down.
|
|
69
|
+
|
|
70
|
+
### How it works
|
|
71
|
+
|
|
72
|
+
Each built-in component (Text, Quest, CanvasBlock, Upload, etc.) registers a **simulate function** and **default simulators**. The simulate function contains the trial logic. The simulators are replaceable decision functions that model participant behavior at each interaction point.
|
|
73
|
+
|
|
74
|
+
For example, Quest's simulate function iterates through questions and calls `simulators.answerQuestion()` for each one. The default `answerQuestion` picks random valid answers. You can override it to model specific participant behavior.
|
|
75
|
+
|
|
76
|
+
### Overriding simulators on a trial
|
|
77
|
+
|
|
78
|
+
Add a `simulators` property to any timeline item to override specific decision functions:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
{
|
|
82
|
+
type: 'PlainInput',
|
|
83
|
+
props: { content: <p>What is your name?</p> },
|
|
84
|
+
simulators: {
|
|
85
|
+
respond: (_trialProps, participant) => ({
|
|
86
|
+
value: participant.nickname,
|
|
87
|
+
participantState: participant,
|
|
88
|
+
}),
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The override is merged with the registered defaults — you only need to specify the decision functions you want to change.
|
|
94
|
+
|
|
95
|
+
### Custom components
|
|
96
|
+
|
|
97
|
+
Register a simulation for your custom components using `registerSimulation`:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
registerSimulation('MyTrial',
|
|
101
|
+
// Simulate function: uses shared trial logic + decision functions
|
|
102
|
+
(trialProps, experimentState, simulators, participant) => {
|
|
103
|
+
const choice = simulators.decide(trialProps, participant);
|
|
104
|
+
return { responseData: { choice: choice.value }, participantState: choice.participantState };
|
|
105
|
+
},
|
|
106
|
+
// Default simulators: one per decision point
|
|
107
|
+
{
|
|
108
|
+
decide: (_trialProps, participant) => ({
|
|
109
|
+
value: 'default_choice',
|
|
110
|
+
participantState: participant,
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The simulate function orchestrates the trial logic. The decision functions are the parts where a human would interact — these are what users override to model different participant behaviors.
|
|
117
|
+
|
|
118
|
+
### Hybrid mode
|
|
119
|
+
|
|
120
|
+
During development, you can auto-advance simulated trials while manually interacting with others. Add `?hybridSimulation=true` to the URL during development:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
http://localhost:5173?hybridSimulation=true
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Trials with `simulators` or `simulate: true` defined on them will auto-advance. Trials without them render normally for human interaction.
|
|
127
|
+
|
|
128
|
+
Hybrid mode is enabled by default during development. For production, set `VITE_DISABLE_HYBRID_SIMULATION=true` to disable it regardless of URL parameters.
|
|
129
|
+
|
|
130
|
+
### Built-in simulator decision functions
|
|
131
|
+
|
|
132
|
+
| Component | Decision functions | Default behavior |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| Text | `respond` | Click button, random reaction time |
|
|
135
|
+
| PlainInput | `respond` | Returns `'simulated_input'` |
|
|
136
|
+
| Quest | `answerQuestion` | Random valid answer per question type |
|
|
137
|
+
| CanvasBlock | `respondToSlide` | Random key from `allowedKeys`, random RT |
|
|
138
|
+
| Upload | *(none)* | Builds CSVs and POSTs to backend |
|
|
139
|
+
| StoreUI | *(none)* | Uses field default values |
|
|
140
|
+
| CheckDevice | *(none)* | Returns simulated device info |
|
|
141
|
+
| EnterFullscreen, ExitFullscreen, MicrophoneCheck, ProlificEnding, RequestFilePermission | *(none)* | No-op, advances immediately |
|
|
142
|
+
|
|
143
|
+
|
|
45
144
|
## Development
|
|
46
145
|
|
|
47
146
|
|
|
@@ -56,11 +155,15 @@ Then create a global link (only needs to run once during setup);
|
|
|
56
155
|
npm link
|
|
57
156
|
```
|
|
58
157
|
|
|
59
|
-
Then set up a local testing project:
|
|
158
|
+
Then set up a local testing project (run from the parent directory so it's created as a sibling):
|
|
60
159
|
|
|
61
160
|
```
|
|
62
|
-
|
|
63
|
-
|
|
161
|
+
cd ..
|
|
162
|
+
node reactive/bin/setup.js
|
|
163
|
+
cd <project-name>
|
|
164
|
+
npm pkg set dependencies.@adriansteffan/reactive="*"
|
|
165
|
+
npm i && npm i --prefix backend
|
|
166
|
+
npm link @adriansteffan/reactive
|
|
64
167
|
```
|
|
65
168
|
|
|
66
169
|
|