@adriansteffan/reactive 0.1.0 → 0.1.2
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/README.md +10 -0
- package/dist/{mod-D9lwPIrH.js → mod-Bb_FAy0j.js} +14863 -8878
- package/dist/mod.d.ts +132 -0
- package/dist/reactive.es.js +47 -42
- package/dist/reactive.umd.js +50 -42
- package/dist/style.css +1 -1
- package/dist/{web-DUIQX1PV.js → web-BfWhpr9p.js} +1 -1
- package/dist/{web-DXP3LAJm.js → web-DmsP-pmx.js} +1 -1
- package/package.json +2 -1
- package/rdk_doc.md +341 -0
- package/src/components/canvasblock.tsx +2 -8
- package/src/components/experimentrunner.tsx +5 -1
- package/src/components/index.ts +4 -0
- package/src/components/mobilefilepermission.tsx +2 -0
- package/src/components/randomdotkinetogram.tsx +1013 -0
- package/src/components/tutorial.tsx +232 -0
- package/src/mod.tsx +1 -1
- package/src/utils/upload.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adriansteffan/reactive",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@tanstack/react-query": "^5.61.3",
|
|
44
44
|
"@tanstack/react-query-devtools": "^5.61.3",
|
|
45
45
|
"@zip.js/zip.js": "^2.7.53",
|
|
46
|
+
"motion": "^12.38.0",
|
|
46
47
|
"react": "^18.2.0",
|
|
47
48
|
"react-dom": "^18.2.0",
|
|
48
49
|
"react-icons": "^5.1.0",
|
package/rdk_doc.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Random Dot Kinematogram (RDK) Component
|
|
2
|
+
|
|
3
|
+
Mostly AI generated reference so I dont have to look at the code in case I forget something minor.
|
|
4
|
+
Will have to have a manual go at all of this to create an actually decent reference.
|
|
5
|
+
|
|
6
|
+
## Basic Usage
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
{
|
|
10
|
+
name: 'rdk_trial',
|
|
11
|
+
type: 'RandomDotKinematogram',
|
|
12
|
+
props: {
|
|
13
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
14
|
+
correctResponse: 'arrowright',
|
|
15
|
+
duration: 2000,
|
|
16
|
+
direction: 90,
|
|
17
|
+
coherence: 0.5,
|
|
18
|
+
},
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Parameters
|
|
23
|
+
|
|
24
|
+
### Trial Control
|
|
25
|
+
|
|
26
|
+
| Parameter | Type | Default | Description |
|
|
27
|
+
|-----------|------|---------|-------------|
|
|
28
|
+
| `validKeys` | `string[]` | `[]` | Valid keyboard responses. Empty array = any key valid |
|
|
29
|
+
| `correctResponse` | `string \| string[]` | `undefined` | The correct response(s) for this trial |
|
|
30
|
+
| `duration` | `number` | `1000` | Duration in ms that dots are shown (response window). Total trial time = `fixationTime + duration`. Use `-1` for infinite duration |
|
|
31
|
+
| `stimulusDuration` | `number` | `undefined` | How long to show stimulus in ms. Defaults to `duration`. Set lower than `duration` to hide stimulus while still accepting responses |
|
|
32
|
+
| `responseEndsTrial` | `boolean` | `true` | Whether response ends the trial immediately |
|
|
33
|
+
|
|
34
|
+
### Dot Motion
|
|
35
|
+
|
|
36
|
+
| Parameter | Type | Default | Description |
|
|
37
|
+
|-----------|------|---------|-------------|
|
|
38
|
+
| `dotCount` | `number` | `300` | Number of dots per frame |
|
|
39
|
+
| `dotSetCount` | `number` | `1` | Number of dot sets to cycle through (reduces tracking artifacts) |
|
|
40
|
+
| `direction` | `number` | `0` | Direction in degrees (0=up, 90=right, 180=down, 270=left) |
|
|
41
|
+
| `coherence` | `number` | `0.5` | Proportion of dots moving coherently (0-1) |
|
|
42
|
+
| `opposite` | `number` | `0` | Proportion moving in opposite direction (0 to 1-coherence) |
|
|
43
|
+
| `speed` | `number` | `60` | Pixels per second (frame-rate independent) |
|
|
44
|
+
| `dotLifetime` | `number` | `-1` | Milliseconds before dot is replaced (-1 = infinite) |
|
|
45
|
+
| `updateRate` | `number` | `undefined` | Update rate in Hz. `undefined` = update every frame |
|
|
46
|
+
|
|
47
|
+
### Dot Appearance
|
|
48
|
+
|
|
49
|
+
| Parameter | Type | Default | Description |
|
|
50
|
+
|-----------|------|---------|-------------|
|
|
51
|
+
| `dotRadius` | `number` | `2` | Radius of dots in pixels (for circles) or base size for characters |
|
|
52
|
+
| `dotCharacter` | `string` | `undefined` | Optional character/emoji to display instead of circles. If set, dots are rendered as text |
|
|
53
|
+
| `dotColor` | `string` | `"white"` | Color of dots (any CSS color) |
|
|
54
|
+
| `coherentDotColor` | `string` | `undefined` | Optional color for coherent dots. If specified, coherent dots will use this color instead of `dotColor` |
|
|
55
|
+
| `backgroundColor` | `string` | `"gray"` | Background color (any CSS color) |
|
|
56
|
+
|
|
57
|
+
### Aperture
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Default | Description |
|
|
60
|
+
|-----------|------|---------|-------------|
|
|
61
|
+
| `apertureShape` | `'circle' \| 'ellipse' \| 'square' \| 'rectangle'` | `'ellipse'` | Shape of the aperture |
|
|
62
|
+
| `apertureWidth` | `number` | `600` | Width in CSS pixels (diameter for circle). Automatically scales for retina displays |
|
|
63
|
+
| `apertureHeight` | `number` | `400` | Height in CSS pixels (ignored for circle/square). Automatically scales for retina displays |
|
|
64
|
+
| `apertureCenterX` | `number` | `window.innerWidth/2` | X-coordinate of aperture center in CSS pixels |
|
|
65
|
+
| `apertureCenterY` | `number` | `window.innerHeight/2` | Y-coordinate of aperture center in CSS pixels |
|
|
66
|
+
| `reinsertMode` | `'random' \| 'opposite' \| 'oppositeSimple' \| 'wrap'` | `'opposite'` | How to reinsert out-of-bounds dots (see below) |
|
|
67
|
+
|
|
68
|
+
### Reinsertion Modes
|
|
69
|
+
|
|
70
|
+
When a dot exits the aperture boundary, `reinsertMode` controls where it reappears:
|
|
71
|
+
|
|
72
|
+
| Mode | Circle/Ellipse | Rectangle/Square |
|
|
73
|
+
|------|----------------|------------------|
|
|
74
|
+
| `'random'` | Random position inside aperture | Random position inside aperture |
|
|
75
|
+
| `'opposite'` (default) | Ray-cast: traces backward along movement trajectory to find entry point | Ray-cast: traces backward along movement trajectory to find entry point |
|
|
76
|
+
| `'oppositeSimple'` | Mirror through center (clamped to boundary) | Edge-based: appears at opposite edge, preserving the other coordinate |
|
|
77
|
+
| `'wrap'` | Toroidal wrap on bounding box (x/y wrap independently) | Toroidal wrap on bounding box (x/y wrap independently) |
|
|
78
|
+
|
|
79
|
+
**`'opposite'` mode** (recommended): Uses ray-casting to find where the dot would have entered if it had continued from the opposite side. This maintains the coherent motion direction across the aperture boundary, producing more natural-looking motion.
|
|
80
|
+
|
|
81
|
+
**`'oppositeSimple'` mode**: A simpler algorithm that doesn't account for movement direction:
|
|
82
|
+
- For circles/ellipses: mirrors the dot position through the center
|
|
83
|
+
- For rectangles: places the dot at the opposite edge while preserving the non-crossing coordinate
|
|
84
|
+
|
|
85
|
+
**`'wrap'` mode**: Toroidal wrapping on the bounding box. X and Y coordinates wrap independently via modulo arithmetic. Preserves relative dot positions for axis-aligned motion. For diagonal motion, the pattern tiles but trajectories appear discontinuous at wrap boundaries.
|
|
86
|
+
|
|
87
|
+
### RDK Algorithm
|
|
88
|
+
|
|
89
|
+
| Parameter | Type | Default | Description |
|
|
90
|
+
|-----------|------|---------|-------------|
|
|
91
|
+
| `noiseMovement` | `'randomTeleport' \| 'randomWalk' \| 'randomDirection'` | `'randomDirection'` | How noise dots move (see below) |
|
|
92
|
+
| `reassignEveryMs` | `number` | `undefined` | Time-based reassignment of dot roles. `undefined` = never reassign (fixed), `0` = reassign every update, `> 0` = reassign every X milliseconds |
|
|
93
|
+
|
|
94
|
+
### Fixation Cross
|
|
95
|
+
|
|
96
|
+
| Parameter | Type | Default | Description |
|
|
97
|
+
|-----------|------|---------|-------------|
|
|
98
|
+
| `showFixation` | `boolean` | `false` | Show fixation cross |
|
|
99
|
+
| `fixationTime` | `number` | `500` | Duration in ms to show fixation before dots appear. Added to `duration` for total trial time |
|
|
100
|
+
| `fixationWidth` | `number` | `15` | Width in pixels |
|
|
101
|
+
| `fixationHeight` | `number` | `15` | Height in pixels |
|
|
102
|
+
| `fixationColor` | `string` | `"white"` | Color |
|
|
103
|
+
| `fixationThickness` | `number` | `2` | Line thickness |
|
|
104
|
+
|
|
105
|
+
### Border
|
|
106
|
+
|
|
107
|
+
| Parameter | Type | Default | Description |
|
|
108
|
+
|-----------|------|---------|-------------|
|
|
109
|
+
| `showBorder` | `boolean` | `false` | Show aperture border |
|
|
110
|
+
| `borderWidth` | `number` | `1` | Border width in pixels |
|
|
111
|
+
| `borderColor` | `string` | `"black"` | Border color |
|
|
112
|
+
|
|
113
|
+
### Noise Movement & Dot Assignment
|
|
114
|
+
|
|
115
|
+
The RDK algorithm is controlled by two orthogonal parameters:
|
|
116
|
+
|
|
117
|
+
**`noiseMovement`** - How noise dots move:
|
|
118
|
+
- `'randomTeleport'`: Noise dots jump to random locations each frame
|
|
119
|
+
- `'randomWalk'`: Noise dots move in a new random direction each frame
|
|
120
|
+
- `'randomDirection'`: Each noise dot has its own consistent random direction (default)
|
|
121
|
+
|
|
122
|
+
**`reassignEveryMs`** - How often dot roles are reassigned:
|
|
123
|
+
- `undefined` (default): Each dot is permanently designated as signal (coherent/opposite) or noise
|
|
124
|
+
- `0`: Dots are randomly assigned to signal/noise each update
|
|
125
|
+
- `> 0`: Dots keep their assignment for the specified milliseconds, then get reassigned
|
|
126
|
+
|
|
127
|
+
**Timing consistency**: This parameter uses wall-clock time, independent of `updateRate` and screen refresh rate. This ensures consistent reassignment timing across all devices:
|
|
128
|
+
- `reassignEveryMs: 100` → reassigns every 100ms regardless of refresh rate
|
|
129
|
+
- Works correctly on 60Hz, 120Hz, or any screen refresh rate
|
|
130
|
+
|
|
131
|
+
| noiseMovement | reassignEveryMs | Behavior |
|
|
132
|
+
|---------------|-----------------|----------|
|
|
133
|
+
| `'randomTeleport'` | `undefined` | Fixed dots, noise jumps randomly |
|
|
134
|
+
| `'randomWalk'` | `undefined` | Fixed dots, noise walks randomly |
|
|
135
|
+
| `'randomDirection'` | `undefined` | Fixed dots, noise has consistent random direction |
|
|
136
|
+
| `'randomTeleport'` | `0` | Dynamic assignment every update, noise jumps randomly |
|
|
137
|
+
| `'randomWalk'` | `0` | Dynamic assignment every update, noise walks randomly |
|
|
138
|
+
| `'randomDirection'` | `0` | Dynamic assignment every update, noise has consistent random direction |
|
|
139
|
+
| Any | `100` | Reassign every 100 milliseconds |
|
|
140
|
+
|
|
141
|
+
## Data Returned
|
|
142
|
+
|
|
143
|
+
The component returns the following data object when the trial ends:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
{
|
|
147
|
+
rt: number | null, // Reaction time in ms (null if no response)
|
|
148
|
+
response: string | null, // Key pressed (null if no response)
|
|
149
|
+
correct: boolean | null, // Whether response was correct (null if no correctResponse specified)
|
|
150
|
+
duration: number, // Duration the trial was set to run
|
|
151
|
+
direction: number, // Direction of coherent motion
|
|
152
|
+
coherence: number, // Coherence level
|
|
153
|
+
framesDisplayed: number // Number of frames actually displayed
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
### Simple Left/Right Motion Discrimination
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
{
|
|
163
|
+
name: 'motion_discrimination',
|
|
164
|
+
type: 'RandomDotKinematogram',
|
|
165
|
+
props: {
|
|
166
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
167
|
+
correctResponse: 'arrowright',
|
|
168
|
+
duration: 2000,
|
|
169
|
+
|
|
170
|
+
direction: 90, // Rightward
|
|
171
|
+
coherence: 0.7, // 70% coherence
|
|
172
|
+
dotCount: 150,
|
|
173
|
+
dotRadius: 3,
|
|
174
|
+
speed: 120, // Pixels per second
|
|
175
|
+
|
|
176
|
+
apertureShape: 'circle',
|
|
177
|
+
apertureWidth: 500,
|
|
178
|
+
|
|
179
|
+
showFixation: true,
|
|
180
|
+
showBorder: true,
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### High Difficulty Task
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
{
|
|
189
|
+
name: 'difficult_trial',
|
|
190
|
+
type: 'RandomDotKinematogram',
|
|
191
|
+
props: {
|
|
192
|
+
coherence: 0.1, // Only 10% coherent
|
|
193
|
+
direction: 180, // Downward
|
|
194
|
+
duration: 3000,
|
|
195
|
+
|
|
196
|
+
dotCount: 300,
|
|
197
|
+
speed: 60, // Slower movement (pixels per second)
|
|
198
|
+
|
|
199
|
+
noiseMovement: 'randomWalk',
|
|
200
|
+
reassignEveryMs: 0, // reassign every update (harder variant)
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Four-Direction Choice
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
{
|
|
209
|
+
name: 'four_direction',
|
|
210
|
+
type: 'RandomDotKinematogram',
|
|
211
|
+
props: {
|
|
212
|
+
validKeys: ['arrowup', 'arrowdown', 'arrowleft', 'arrowright'],
|
|
213
|
+
correctResponse: 'arrowup',
|
|
214
|
+
|
|
215
|
+
direction: 0, // Upward
|
|
216
|
+
coherence: 0.5,
|
|
217
|
+
duration: 2500,
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Opposite Motion
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
{
|
|
226
|
+
name: 'opposite_motion',
|
|
227
|
+
type: 'RandomDotKinematogram',
|
|
228
|
+
props: {
|
|
229
|
+
direction: 90, // Rightward
|
|
230
|
+
coherence: 0.4, // 40% rightward
|
|
231
|
+
opposite: 0.4, // 40% leftward
|
|
232
|
+
// 20% random
|
|
233
|
+
|
|
234
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
235
|
+
correctResponse: 'arrowright',
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Staircasing Coherence
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Example of varying coherence across trials
|
|
244
|
+
const coherenceLevels = [0.05, 0.1, 0.2, 0.4, 0.6, 0.8, 0.95];
|
|
245
|
+
|
|
246
|
+
coherenceLevels.forEach((coh, index) => ({
|
|
247
|
+
name: `rdk_${index}`,
|
|
248
|
+
type: 'RandomDotKinematogram',
|
|
249
|
+
props: {
|
|
250
|
+
coherence: coh,
|
|
251
|
+
direction: Math.random() < 0.5 ? 90 : 270, // Random left/right
|
|
252
|
+
duration: 2000,
|
|
253
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
254
|
+
},
|
|
255
|
+
}));
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Colored Coherent Dots
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
{
|
|
262
|
+
name: 'colored_coherent',
|
|
263
|
+
type: 'RandomDotKinematogram',
|
|
264
|
+
props: {
|
|
265
|
+
direction: 90, // Rightward
|
|
266
|
+
coherence: 0.3, // 30% coherent
|
|
267
|
+
|
|
268
|
+
dotColor: 'white', // Incoherent dots are white
|
|
269
|
+
coherentDotColor: 'red', // Coherent dots are red
|
|
270
|
+
backgroundColor: '#1a1a1a',
|
|
271
|
+
|
|
272
|
+
dotCount: 200,
|
|
273
|
+
speed: 120, // Pixels per second
|
|
274
|
+
|
|
275
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
276
|
+
correctResponse: 'arrowright',
|
|
277
|
+
duration: 3000,
|
|
278
|
+
},
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Late Responses (Post-Stimulus)
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
{
|
|
286
|
+
name: 'late_response_trial',
|
|
287
|
+
type: 'RandomDotKinematogram',
|
|
288
|
+
props: {
|
|
289
|
+
duration: 3000, // Total response window: 3 seconds
|
|
290
|
+
stimulusDuration: 1000, // Dots visible for 1 second, then blank
|
|
291
|
+
|
|
292
|
+
direction: 90,
|
|
293
|
+
coherence: 0.7,
|
|
294
|
+
|
|
295
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
296
|
+
correctResponse: 'arrowright',
|
|
297
|
+
|
|
298
|
+
showFixation: true, // Fixation remains visible
|
|
299
|
+
},
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Character & Emoji Dots
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
{
|
|
307
|
+
name: 'emoji_rdk',
|
|
308
|
+
type: 'RandomDotKinematogram',
|
|
309
|
+
props: {
|
|
310
|
+
direction: 90,
|
|
311
|
+
coherence: 0.5,
|
|
312
|
+
|
|
313
|
+
dotCharacter: '🔴', // Use red circle emoji instead of circles!
|
|
314
|
+
dotRadius: 4, // Controls size (font size = dotRadius * 2.5)
|
|
315
|
+
|
|
316
|
+
backgroundColor: '#1a1a1a',
|
|
317
|
+
|
|
318
|
+
validKeys: ['arrowleft', 'arrowright'],
|
|
319
|
+
duration: 2000,
|
|
320
|
+
},
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**More Examples:**
|
|
325
|
+
```typescript
|
|
326
|
+
dotCharacter: '🔴' // Red circle emoji
|
|
327
|
+
dotCharacter: '●' // Filled circle character
|
|
328
|
+
dotCharacter: '■' // Square
|
|
329
|
+
dotCharacter: '★' // Star
|
|
330
|
+
dotCharacter: 'X' // Letter X
|
|
331
|
+
dotCharacter: '🐝' // Bee emoji
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**With Coherent Colors:**
|
|
335
|
+
```typescript
|
|
336
|
+
{
|
|
337
|
+
dotCharacter: '●',
|
|
338
|
+
dotColor: 'white',
|
|
339
|
+
coherentDotColor: 'red', // Coherent characters will be red!
|
|
340
|
+
}
|
|
341
|
+
```
|
|
@@ -15,15 +15,9 @@ import {
|
|
|
15
15
|
} from '../utils/bytecode';
|
|
16
16
|
import { BaseComponentProps, isFullscreen, now } from '../utils/common';
|
|
17
17
|
import { registerSimulation, ParticipantState } from '../utils/simulation';
|
|
18
|
-
import { registerFlattener } from '../utils/upload';
|
|
18
|
+
import { registerFlattener, arrayFlattener } from '../utils/upload';
|
|
19
19
|
|
|
20
|
-
registerFlattener('CanvasBlock', 'canvas',
|
|
21
|
-
const responseData = item.responseData;
|
|
22
|
-
if (Array.isArray(responseData)) {
|
|
23
|
-
return responseData.map((i) => ({ block: item.name, ...i }));
|
|
24
|
-
}
|
|
25
|
-
return [];
|
|
26
|
-
});
|
|
20
|
+
registerFlattener('CanvasBlock', 'canvas', arrayFlattener);
|
|
27
21
|
|
|
28
22
|
export type SlideSimulatorResult = {
|
|
29
23
|
key: string | null;
|
|
@@ -33,6 +33,8 @@ import CheckDevice from './checkdevice';
|
|
|
33
33
|
import VoicerecorderQuestionComponent from './voicerecorder';
|
|
34
34
|
import React from 'react';
|
|
35
35
|
import StoreUI from './storeui';
|
|
36
|
+
import { Tutorial } from './tutorial';
|
|
37
|
+
import { RandomDotKinematogram } from './randomdotkinetogram';
|
|
36
38
|
|
|
37
39
|
type ComponentsMap = {
|
|
38
40
|
[key: string]: ComponentType<any>;
|
|
@@ -50,7 +52,9 @@ const defaultComponents: ComponentsMap = {
|
|
|
50
52
|
RequestFilePermission,
|
|
51
53
|
CanvasBlock,
|
|
52
54
|
CheckDevice,
|
|
53
|
-
StoreUI
|
|
55
|
+
StoreUI,
|
|
56
|
+
Tutorial,
|
|
57
|
+
RandomDotKinematogram,
|
|
54
58
|
};
|
|
55
59
|
|
|
56
60
|
const defaultCustomQuestions: ComponentsMap = {
|
package/src/components/index.ts
CHANGED
|
@@ -11,3 +11,7 @@ export { default as ExperimentRunner } from './experimentrunner';
|
|
|
11
11
|
export { default as RequestFilePermission } from './mobilefilepermission';
|
|
12
12
|
export { default as CanvasBlock } from './canvasblock';
|
|
13
13
|
export { default as CheckDevice } from './checkdevice';
|
|
14
|
+
export { Tutorial, useTutorialSlide } from './tutorial';
|
|
15
|
+
export type { TutorialProps } from './tutorial';
|
|
16
|
+
export { RandomDotKinematogram, RDKCanvas } from './randomdotkinetogram';
|
|
17
|
+
export type { RDKCanvasProps, RDKCanvasHandle, RDKProps, NoiseMovement } from './randomdotkinetogram';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { BaseComponentProps, getPlatform } from '../utils/common';
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { registerSimulation, noopSimulate } from '../utils/simulation';
|
|
4
|
+
import { registerFlattener } from '../utils/upload';
|
|
4
5
|
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
|
5
6
|
import { Capacitor } from '@capacitor/core';
|
|
6
7
|
|
|
8
|
+
registerFlattener('RequestFilePermission', null);
|
|
7
9
|
registerSimulation('RequestFilePermission', noopSimulate, {});
|
|
8
10
|
|
|
9
11
|
export default function RequestFilePermission({ next }: BaseComponentProps) {
|